From c6aa8b1d5f90305574e5ac9366c9964e92da513b Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 06:32:01 -0400 Subject: [PATCH 01/17] docs(964): prepare the engine toggle coordinator 947 review residuals fix Active minor-audit folder with acceptance criteria AC1 to AC8, the atomic plan v1.3 cleared by executor preflight after four rounds, the preflight clearance record, and the promoted potential record for issue 964. Co-Authored-By: Claude Opus 5.5 --- .../preflight-clearance.2026-10-02T07-50.md | 36 + .../issue.md | 86 ++ .../plan.2026-10-02T05-20.md | 970 ++++++++++++++++++ ...toggle-coordinator-947-review-residuals.md | 69 ++ 4 files changed, 1161 insertions(+) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md create mode 100644 docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md new file mode 100644 index 000000000..2b1223012 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md @@ -0,0 +1,36 @@ +# Preflight Clearance — Issue #964 + +- Timestamp: 2026-10-02T07-50 +- Issue: #964 (engine-toggle-coordinator-947-review-residuals) +- Branch: bug/engine-toggle-coordinator-947-review-residuals-964 +- Base: origin/main at 94287369908cc920b21b0e3256314f988ad7d2f5 +- Work Mode: minor-audit +- Plan: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md (version 1.3) +- Plan blob SHA cleared: 7f46d91cb534de5b0abade1af87f0b3825a85433 +- Plan validator: mcp__drm-copilot__validate_orchestration_artifacts (artifact_type plan) returned ok with no warnings on the cleared blob. + +## Signal + +PREFLIGHT: ALL CLEAR + +CONVERGENCE: NO FURTHER ROUNDS EXPECTED + +## Rounds + +- Round count: 4 +- Round 1 (plan version 1.0, blob 9ca203e423940ed37cf386b1f453f13eb5fe11d6): PREFLIGHT: REVISIONS REQUIRED, 3 defects. + - The pre-existing-failure comparison for AC8 read short trx test names; replaced by a fully qualified name set (`FAILED-FQN` rows) compared verbatim against the Phase 0 baseline. + - P1-T5, P1-T11 and P1-T21 ran commands without naming an evidence artifact; each now creates its evidence file and the sibling tasks append to it. + - The host-path hygiene sweep ran before the last artifacts were written; P2-T20 adds a final sweep. +- Round 2 (plan version 1.1, blob eadf8998db4e040fe3d09891c120fcee65d7ca9e): PREFLIGHT: ALL CLEAR, 0 defects (full-plan re-check). +- Orchestrator delta after round 2 (plan version 1.2, blob 50a7ab21390138005989f654c51d33cb645adabf): P2-T19 asserted that no file outside the Write Set evidence list exists under the evidence folder, which this committed record would contradict. The plan now names this record as a preparation-phase file, pins its blob at P0-T2 and checks it unchanged at P2-T19. +- Round 3 (plan version 1.2): PREFLIGHT: REVISIONS REQUIRED, 3 defects. + - `CLEARANCE-PATH:` must be recorded in forward-slash form, because `git rev-parse HEAD:` does not resolve a backslash-separated path. + - The hygiene sweep printed totals only, so a hit could not be attributed to a file; it now prints one `HIT-FILE` row per file with a non-zero count. + - The P2-T9 and P2-T20 lower bounds now count this record (33 and 36), so a single missing evidence file fails them. +- Round 4 (plan version 1.3, blob 7f46d91cb534de5b0abade1af87f0b3825a85433): PREFLIGHT: ALL CLEAR, 0 defects. + +## Execution Notes + +- This record is committed by the preparation run before execution and must not be modified afterwards: P0-T2 records its blob and P2-T19 requires the working-tree hash to match. +- The executor must be dispatched without worktree isolation, because the Bash-tool isolation guard refuses every `pwsh` invocation in an isolated agent and every toolchain step in the plan is a pwsh payload. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md new file mode 100644 index 000000000..4dab5ba7e --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md @@ -0,0 +1,86 @@ +# engine-toggle-coordinator-947-review-residuals (Issue #964) + +- Date captured: 2026-10-01 +- Author: Dan Moisan +- Status: Active -> docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/ (Issue #964) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #964 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/964 +- Last Updated: 2026-10-01 +- Work Mode: minor-audit + +## Summary + +The #947 review (PR #963) left three residuals in `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`: +1. On the refusal path of `HandleToggleClickAsync`, the "engines unavailable" notification call is still unguarded. A throwing notification sink can escape into the Office ribbon callback, so the method's "never throws" comment overstates that path. This is the same root cause as #947. +2. The `GetPrimeTask` doc comment opens with "The prime task", but the method returns the registration marker. +3. The file is 476 of 500 lines, so it needs splitting before its next change. + +Re-measured on origin/main at 942873699 (2026-10-02): the file is 496 of 500 lines after #948, and the refusal-path call sits at line 186. All three residuals are still outstanding. + +## Acceptance Criteria + +Each criterion below is falsifiable by the named test or measurement. Related defects in the same file are folded in under the 2026-10-02 related-defect remediation directive (criteria AC6 and AC7). + +- [ ] AC1 (refusal-path notification guard, regression-first): with the engines accessor returning null and a `notifyUnavailable` sink that throws, `EngineToggleStateCoordinator.HandleToggleClickAsync` completes without throwing. A new MSTest regression test proves this; it is recorded failing against the unmodified coordinator (fail-before evidence under `evidence/regression-testing/`) and passing after the fix. +- [ ] AC2 (notification failure is reported, not lost): in the AC1 scenario the notification sink is attempted exactly once, the sink's exception is delivered exactly once to `logError` (the same exception instance), no engine member is invoked, and no control is invalidated. When `logError` also throws in that scenario, `HandleToggleClickAsync` still completes without throwing. Both behaviours are asserted by named MSTest tests. +- [ ] AC3 (one shared sink guard): all three sink call sites (refusal-path `notifyUnavailable`, click-boundary `logError`, prime-fault `logError` in `CompletePrime`) route through a single private guard helper; no other `catch` clause that discards a sink exception remains in the coordinator's source files. Verified by reading the split source and by the existing #947 tests in `EngineToggleStateCoordinatorTests.ThrowingSink.cs` passing unchanged. +- [ ] AC4 (prime-marker ordering invariants preserved across the split): the #942/#944/#947/#948 invariants still hold: the marker is registered before the prime starts; a prime fault is reported before the marker is cleared; a sink that throws leaves the fault kind unrecorded (report still owed); a repeated fault kind for the same engine is reported once. Verified by every existing test in the `EngineToggleStateCoordinatorTests` partials (`.cs`, `.Race.cs`, `.PrimeFaultOrdering.cs`, `.PrimeRegistration.cs`, `.ThrowingSink.cs`, `.RepeatFaultSuppression.cs`) passing with no assertion weakened or removed. +- [ ] AC5 (`GetPrimeTask` documentation accuracy): the `GetPrimeTask` `` and `` describe the registration marker (completed only after the prime outcome has been observed and reported), not "the prime task" or "the in-flight prime". Verified by reading the doc comment. +- [ ] AC6 (file-size split): `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` is split into cohesive partial-class files of the same `internal sealed partial class EngineToggleStateCoordinator`, each file at or below 450 lines, each new file registered as a `Compile` item in `TaskMaster/TaskMaster.csproj`; every touched test file stays at or below 500 lines. Verified by a line count of every coordinator source file and test file and by the build compiling the new files. +- [ ] AC7 (comment drift in touched files): every comment that counts or locates the coordinator's `catch` clauses (the `HandleToggleClickAsync` summary and remarks, the `StartObservedPrime` remarks, the `CompletePrime` remarks) and the `HandleToggleClickAsync` "never throws" remark match the post-change code, and the constructor's `notifyUnavailable` and `enginesAccessor` parameter docs state the guarded behaviour and the accessor's non-throwing precondition. Verified by reading each named comment against the code. +- [ ] AC8 (toolchain and coverage): the CLAUDE.md C# toolchain passes in one clean pass (csharpier check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild` without `/p:Nullable=enable`, `Invoke-MSTestWithCoverage.ps1`), with no new failing test relative to baseline and the coordinator's line coverage not lower than its baseline figure. + +## Environment + +- OS/version: Windows 11 (Outlook VSTO add-in) +- Python version: n/a (C#, .NET Framework 4.8) +- Command/flags used: review of PR #963 +- Data source or fixture: n/a + +## Steps to Reproduce + +1. Construct the coordinator with engines unavailable and a notification delegate that throws. +2. Invoke the toggle click. + +## Expected Behavior + +- The click handler never throws into the ribbon callback on any path. +- Doc comments match behavior. +- The file has room under the 500-line limit. + +## Actual Behavior + +- The notification exception escapes on the refusal path. +- The `GetPrimeTask` doc is inaccurate. +- The file is 476 lines. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: PR #963 body, Follow-ups 1 to 3; #947 review artifacts. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [x] Medium +- [ ] Low + +## Suspected Cause / Notes + +#947 guarded the two `_logError` call sites only. The duplicated guard could become one helper that is applied to both log sinks and notification sinks. + +## Proposed Fix / Validation Ideas + +- [ ] Write a regression test first: a throwing notification on the refusal path must not escape. Then add a shared "invoke sink safely" helper and use it at all three sites. +- [ ] Correct the `GetPrimeTask` doc comment. +- [ ] Split the file into partials before or with the change, keeping each partial under 500 lines. +- [ ] Sequence this with #948, which edits the same file. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md new file mode 100644 index 000000000..7f46d91cb --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -0,0 +1,970 @@ +# 2026-10-01-engine-toggle-coordinator-947-review-residuals (Plan) + +- **Issue:** #964 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Work Mode:** minor-audit (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` line 12 reads `- Work Mode: minor-audit`) +- **Last Updated:** 2026-10-02T07-40 +- **Status:** Draft, revision round 3 applied (preflight deltas: forward-slash `CLEARANCE-PATH:`, per-file `HIT-FILE` attribution in CMD-HYGIENE, preparation-phase record counted in the P2-T9 and P2-T20 lower bounds), awaiting executor preflight +- **Version:** 1.3 (revision round 3: P0-T2 records `CLEARANCE-PATH:` in repository-relative forward-slash form and P2-T19 compares the Glob result after the same conversion; CMD-HYGIENE prints one `HIT-FILE` row per file with a non-zero host count, and P2-T9 and P2-T20 repair or stop by those rows; the P2-T9 lower bound becomes 33 and the P2-T20 lower bound becomes 36, each now counting the preparation-phase record, superseding the version 1.2 wording that scanned it outside the bounds). Version 1.2 (revision round 2: the `## Write Set` names the preparation-phase record `other/preflight-clearance..md`; P0-T2 records `CLEARANCE-PATH:` and `CLEARANCE-BLOB:`; P2-T19 admits exactly that one record and requires its working-tree hash to equal `CLEARANCE-BLOB:`; P2-T9 and P2-T20 state that the record is scanned outside their lower bounds and is never repaired by this plan). Version 1.1 (revision round 1: the pre-existing-failure comparison uses fully qualified test names (D1); P1-T5, P1-T11 and P1-T21 name and create their evidence artifacts (D2); a final host-path sweep P2-T20 runs after the last artifact is written (D3)). Version 1.0 was the initial authoring; the scaffold that occupied this path was replaced in full. +- **Plan path continuity:** this file is updated in place for every revision round. No timestamped sibling plan file is created for this cycle. +- **Execution topology:** The executor for this plan must be dispatched without worktree isolation: the Bash-tool isolation guard refuses every `pwsh` invocation in an isolated agent, and every toolchain step in this plan is a pwsh payload. + +**Fail-closed evidence rule:** every command-bearing task writes one evidence artifact carrying `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`. A task whose artifact is missing or incomplete stays unchecked, and the plan outcome is BLOCKED or INCOMPLETE, never PASS. Baseline, final-QC and coverage-comparison artifacts are mandatory; a missing one makes the audit verdict BLOCKED. + +**Evidence accounting rule:** the artifact path is named in each task. Do not mark an evidence-bearing task complete without the artifact on disk at that exact path. + +**Evidence location:** every artifact lives under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/` in the canonical sub-kinds `baseline/`, `regression-testing/`, `qa-gates/` and `other/`. EVIDENCE_LOCATION_OVERRIDE_REJECTED: none supplied. In task text the token FEATURE abbreviates `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964`. + +## Requirement sources + +- Sole requirements source: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`, section `## Acceptance Criteria` (line 23), eight checkbox lines `- [ ] AC1` through `- [ ] AC8` (lines 27 to 34 when this plan was authored; check-off tasks locate each line by its `- [ ] ACn (` prefix, never by line number). Only that section is an acceptance-criteria source. No `spec.md`, `user-story.md` or `research.md` exists in the feature folder or is required; P0-T2 fails closed if one appears. +- Predecessors (merged into the base): issues #942, #944, #947 and #948, whose invariants AC4 preserves. +- Base: origin/main at `94287369908cc920b21b0e3256314f988ad7d2f5` (BASE-SHA). The branch `bug/engine-toggle-coordinator-947-review-residuals-964` was cut from it. Every `git diff` and `git show` in this plan uses that literal as its ref operand. + +## AC identity table + +| ID | Subject | Proved by | +|---|---|---| +| AC1 | Refusal-path notification guard, regression-first | `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` failing at P1-T14, passing at P1-T24 | +| AC2 | Notification failure reported once, nothing invoked; double throw contained | `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing`, `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` | +| AC3 | One shared sink guard | P1-T22 stripped census; the four #947 tests in `.ThrowingSink.cs` passing unchanged | +| AC4 | Ordering invariants preserved across the split | P1-T8 and P1-T24 fixture runs; P1-T25 unchanged-partials gate; `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` | +| AC5 | `GetPrimeTask` documentation accuracy | P1-T22 phrase census | +| AC6 | File-size split | P1-T26 line counts and csproj registration; P1-T7 census; builds at P1-T8, P1-T23, P2-T3 and P2-T4 | +| AC7 | Comment drift in touched files | P1-T22 phrase census | +| AC8 | Toolchain and coverage | Phase 2 loop, P2-T6 comparison | + +## Verified tree facts (re-derived in this worktree for version 1.0; Phase 0 re-checks each one) + +1. `.git` of the worktree names the branch `bug/engine-toggle-coordinator-947-review-residuals-964`; that branch ref and `refs/remotes/origin/main` both resolve to BASE-SHA, so HEAD equals the base when this plan was authored. +2. `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` is 496 lines (CRLF in the working tree). Usings at 1 to 6: `System`, `System.Collections.Concurrent`, `System.Globalization`, `System.Threading`, `System.Threading.Tasks`, `UtilitiesCS`. Line 45 `internal sealed class EngineToggleStateCoordinator`; 46 `{`; 47 to 51 the `NullEngineNameToken` summary and constant; 52 blank; 53 `_enginesAccessor`; constructor parameter docs `enginesAccessor` 95 to 99, `notifyUnavailable` 104 to 107, `logError` 108 to 111; `GetPressed` returns 134 to 138; `HandleToggleClickAsync` summary 162 to 165 (`The other two are sink guards` at 164), remarks 171 to 181, signature 182, refusal call `_notifyUnavailable(BuildUnavailableMessage(engineName));` at 186, click-boundary sink guard 194 to 204; `ExecuteToggleAsync` closing brace 255; 256 blank; `GetPrimeTask` summary 257 to 260 (`The in-flight — or most recently completed — prime` at 258), returns 262 to 269 (`The prime task, or` at 263), signature 270; marker registration 303 to 306; `StartObservedPrime` remarks 314 to 324 (`The three` at 315), `marker.SetResult(true)` in `finally` at 340 to 343; `CompletePrime` summary 373 to 378, remarks 379 to 404, signature 405, guarded report 421 to 432 with `_reportedPrimeFaults[reportKey] = 0;` at 426 directly after the sink call inside the `try`, `TryRemove` 434, closing brace 435; 436 blank; `RenderEngineName` summary 437, signature 440; `BuildUnmappedKeyMessage` closing brace 494; 495 ` }`; 496 `}`. Code `catch` lines: 194, 200, 428. +3. `TaskMaster/Ribbon/RibbonCommandBoundary.cs` `ReportFailure` 80 to 94 (presentation failure forwarded to `SafeLog`) and `SafeLog` 103 to 113 (log failure discarded): the pattern AC2 adopts. +4. `TaskMaster/Ribbon/RibbonController.EngineCommands.cs` constructs the coordinator at 67 to 77 and forwards at 103 to 106; its returns text (98 to 102, "The returned task never faults") becomes true on every path after the fix; it is not edited. +5. `TaskMaster/TaskMaster.csproj` (legacy, explicit compile items, CRLF) carries `` at 466 followed by `Ribbon\RibbonController.cs` at 467. `TaskMaster.Test/TaskMaster.Test.csproj` carries the six fixture entries at 352 and 359 to 363 (`.RepeatFaultSuppression.cs` at 363, followed by `EngineTogglePressedStateCacheTests.cs` at 364). An unlisted file is not compiled. +6. Fixture partials under `TaskMaster.Test/Ribbon/`: `EngineToggleStateCoordinatorTests.cs` 470 lines (`[TestClass]` 22, `private sealed class Harness` 403, notify sink `message => Notifications.Add(message),` at 414, `OnLogError` property 445, `Notifications` 449, `Errors` 451, `LoggedError` 457); `.Race.cs` 277 (stale remark 196 to 201: "logs a second error", false since #948 suppresses the repeat); `.PrimeFaultOrdering.cs` 77; `.PrimeRegistration.cs` 175; `.ThrowingSink.cs` 215; `.RepeatFaultSuppression.cs` 290. Census: 36 `[TestMethod]`, 1 `[DataTestMethod]`, 3 `[DataRow(`, so 39 executed cases (the #948 pass-after run observed `COUNTERS total=39`). The harness has no notification hook. +7. `UtilitiesCS/Interfaces/IGlobals/IAppItemEngines.cs` declares `IAppItemEngines` in namespace `UtilitiesCS` (line 5). `TaskMaster/TaskMaster.csproj` sets `LangVersion` `preview` (31) and generates no documentation file, so `cref` targets are not compiled into diagnostics. `.editorconfig` raises only `MSTEST0032` to warning (29); analyzer rules are suggestions. +8. `scripts/vscode/Invoke-MSTestWithCoverage.ps1`: parameters `SearchRoot`, `Configuration`, `CoverageOutput` (default `coverage\coverage.cobertura.xml`, 9); the inner vstest arguments hard-code `/TestCaseFilter:TestCategory!=LiveOutlook` (91), the results directory and the trx name (92, 93; fixed values `coverage\test-results` and `mstest-coverage-run.trx` at 297 and 298); a non-zero collector exit throws `MSTest with coverage failed with exit code N` (262) before post-processing, so the raw document and the trx stay on disk and no summary or projection is written; assembly discovery excludes `(^|\\)\.claude\\` relative to the search root (353), so the runner discovers this worktree's own test assemblies; on success it prints the `First-party coverage:` line (410), writes the JaCoCo projection beside the output (415 to 419), writes `coverage\test-results\mstest-coverage-run.summary.txt` (440 to 447) and retains the raw document because it sits in the repository coverage directory (449 to 453, `Test-RawCoverageDocumentRetained`); the entry guard at 459 makes the file safe to dot-source. +9. `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` defines `Get-CoberturaClassLineSummary -ClassNode` (160) and `Merge-CoberturaClassesByFilename` (260, groups class elements by `filename`), applied by `ConvertTo-KoverageCoberturaXml` (407, 442); after post-processing each source file is one class node (the #948 final run read `COORD-CLASS-NODES: 1` for the then single-file coordinator). `Invoke-MSTestWithCoverage.FirstParty.ps1` `Get-CoberturaFirstPartyCoverageReport` (123) renders `First-party coverage: lines a/b (x%), branches c/d (y%)` (117 to 120). `Invoke-MSTest.TrxSummary.ps1` `Format-TrxRunSummary` (103) renders five lines beginning `Test run outcome:`. +10. `scripts/vscode/Install-RepoDotNetSdk.ps1` installs SDK 8.0.205 to `.dotnet-sdk` with marker `.dotnet-sdk\sdk\8.0.205` (3, 56); `scripts/vscode/Invoke-Restore.ps1` exists; `global.json` pins 8.0.205 with paths `.dotnet-sdk` and the host. Neither `.dotnet-sdk\sdk` nor `packages\` exists in this worktree, so bootstrap is required. `.gitignore` ignores `*.trx` (146), `*cobertura*.xml` (147), `coverage/*` (150, `.gitkeep` re-included at 151), `**/[Pp]ackages/*` (197) and `.dotnet*/` (357). `.csharpierignore` excludes `**/evidence/**` (4), raw coverage and trx names (5 to 8), project files (12 to 14), `packages.config` (16) and `app.config` (18). +11. Observed outputs from the executed #947 and #948 runs on this machine (their evidence folders, 2026-10-01): the stall probe over the four UtilitiesCS.Test shell-icon classes exited 1 with one failed test (`GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension`, Win32 icon-handle `ArgumentException`), not a hang; the coordinator fixture fail-before message of a FluentAssertions `NotThrowAsync` assertion read `Did not expect any exception because , but found System.InvalidOperationException: sink failed / at ...`; an exception escaping a test method read `Test method threw exception: / : `; `dotnet tool run csharpier check .` printed `Checked N files in ms.` and named only unformatted paths; `First-party coverage: lines 56204/65855 (85.35%), branches 13618/17078 (79.74%)` and `COORD-LINES covered=177 valid=177` at the #948 final. +12. The four UtilitiesCS.Test shell-icon classes are `UtilitiesCS.Test.HelperClasses.ShellUtilities_Tests`, `UtilitiesCS.Test.HelperClasses.ShellUtilitiesStatic_Tests`, `UtilitiesCS.Test.HelperClasses.SysImageListHelperTests` and `UtilitiesCS.Test.EmailIntelligence.OSBrowser_Tests`; CI runs them unfiltered. +13. `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md` is the promotion record for #964; this plan never edits it. +14. (re-derived in version 1.1) `scripts/vscode/Invoke-MSTest.TrxSummary.ps1` `Get-TrxRunSummary` builds `FailedTestName` from the `testName` attribute of each failed result node (74 to 85), which is the short method name, so two failed tests of the same method name in different classes are indistinguishable in `FAILED-SET:`. `CMD-COVERAGE-POST` therefore resolves each failed result's `testId` against `TestDefinitions/UnitTest/TestMethod` (`className` up to its first comma, a dot, then `name`) and prints `FAILED-FQN` rows; every pre-existing-failure comparison in this plan uses those rows. The runner's trx name and directory are the fixed values at `Invoke-MSTestWithCoverage.ps1` 297 and 298, copied by `CMD-COVERAGE-RUNNER` to `coverage\STAGE-964.trx`. Every fixture partial declares `namespace TaskMaster.Test.Ribbon` and `public partial class EngineToggleStateCoordinatorTests` (primary file 9 and 23 with `[TestClass]` at 22; ThrowingSink 7 and 19), and delivered source T2 uses the same namespace and class, so every fixture test's fully qualified name begins `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.`. + +## Design decisions (do not redesign) + +- **D-1 Split first, behaviour-preserving (AC6).** `EngineToggleStateCoordinator` becomes `internal sealed partial class EngineToggleStateCoordinator` across three files: `EngineToggleStateCoordinator.cs` (class documentation, fields, constructor, `GetPressed`, `HandleToggleClickAsync`, `ExecuteToggleAsync`, and after the fix `TryInvokeSink`), `EngineToggleStateCoordinator.Prime.cs` (`GetPrimeTask`, `StartPrimeIfNeeded`, `StartObservedPrime`, `ApplyPrimeAsync`, `CompletePrime`) and `EngineToggleStateCoordinator.Messages.cs` (`NullEngineNameToken`, `RenderEngineName` and the `Build*Message` helpers). All fields stay in the main file (`_pressedState` is shared by the toggle and prime paths). The split is a pure move: base lines are copied verbatim, only the class keyword gains `partial`, and each file carries exactly the usings its members need. It lands in P1-T2 to P1-T8, before the regression tests are written, and P1-T8 proves the unchanged fixture still passes against it, so the fail-before run at P1-T14 compiles against the split but unfixed coordinator. The split is verified by a multiset census of non-blank trimmed lines against BASE-SHA (P1-T7) whose only admitted differences are the declaration keyword, the two new namespace and class scaffolds and the redistributed usings. +- **D-2 One guard helper (AC3).** `private static bool TryInvokeSink(Action sinkCall, out Exception sinkFailure)` in the main file holds the only sink `catch` in the type: it returns `true` when the sink returned normally and `false` with the exception when it threw, and never rethrows. All four sink invocations route through it: the refusal-path `notifyUnavailable`, the refusal-path `logError` that reports a notification failure, the click-boundary `logError` and the prime-fault `logError` in `CompletePrime`. Afterwards the coordinator source carries exactly two `catch` clauses: the click boundary (which observes an engine fault) and the guard. +- **D-3 #948 invariant inside the guard (AC4).** In `CompletePrime` the record `_reportedPrimeFaults[reportKey] = 0;` is the only statement of the branch taken when `TryInvokeSink` returns `true`, so it executes only when the sink returned normally and directly after it; a throwing sink leaves the fault kind unrecorded and the report owed. It is never placed before the sink call, in an `else` branch, or after the branch. +- **D-4 Refusal path (AC1, AC2).** When the engines accessor yields null the notification goes through the guard; when the guard returns `false`, the captured exception is reported once through the guarded `logError` with the new message `BuildNotifyFailedMessage`; the method returns without invoking any engine member or invalidating any control, and never throws. This follows `RibbonCommandBoundary.ReportFailure`/`SafeLog` (fact 3). +- **D-5 Documentation (AC5, AC7).** `GetPrimeTask` describes the registration marker; the `HandleToggleClickAsync` summary and remarks, the `StartObservedPrime` remarks and the `CompletePrime` summary and remarks match the two-`catch` structure; the constructor `enginesAccessor`, `notifyUnavailable` and `logError` parameter docs state the guarded behaviour and the accessor's non-throwing precondition; the `GetPressed` returns sentence states the same precondition. +- **D-6 Tests (AC1, AC2, AC4).** A new partial `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` holds four tests whose names are fixed here (NEW-NAMES-964). Three carry the fail-before obligation; the fourth (`GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain`) closes a coverage gap found in planning: no existing test asserts that a throwing sink leaves the fault kind unrecorded, which is exactly the record placement this change rewrites. It passes before and after the fix and fails if the record moves ahead of the sink call or out of the success branch. The primary fixture's `Harness` gains one member, `OnNotify`, invoked after the notification is recorded (mirroring `OnLogError`), so a throwing hook both records the attempt and models a throwing sink; no assertion in any existing partial changes. +- **D-7 Folded-in related defects (maintainer directive 2026-10-02).** (a) the `.Race.cs` remark at 196 to 201 claims the re-prime "logs a second error", which #948 made false; the remark is reworded, no code changes. (b) the `GetPressed` returns sentence and the `logError` parameter doc (D-5). (c) the missing throwing-sink-leaves-report-owed test (D-6). No completely unrelated defect was found. +- **D-8 Coverage route and AC8 test-step rule.** Step 4 runs `scripts/vscode/Invoke-MSTestWithCoverage.ps1` verbatim (by absolute path from the worktree cwd, fact 8). The runner's exit code is recorded. AC8's own qualifier ("no new failing test relative to baseline") defines the test-step pass condition: the step passes when the runner exits 0, or when it exits non-zero with `MSTest with coverage failed with exit code` and the trx-derived set of fully qualified failed names (`FAILED-FQN` rows of `CMD-COVERAGE-POST`, each the `TestMethod` `className` up to its first comma, a dot, and the `TestMethod` `name`) is non-empty, `TEST-DEFINITIONS:` is at least 1, no value begins with `UNRESOLVED:`, every value appears verbatim as a `FAILED-FQN` row of `FEATURE/evidence/baseline/coverage-baseline.md`, and no value begins with `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.`. When the runner exits non-zero it writes no summary or projection (fact 8), so `CMD-COVERAGE-POST` post-processes the raw document with the runner's own helpers (`RAW True`); when it exits 0, the document is already post-processed (`RAW False`). Both floors (80% line, 75% branch, applied by the runner's threshold functions) must be met in every case. A run that hangs is bounded by wall clock and stops the plan; no alternative route is taken. +- **D-9 Coordinator coverage figure (AC8).** The coordinator figure is the sum, over every class node whose normalised `filename` ends with one of the three coordinator source paths, of `Get-CoberturaClassLineSummary` covered and valid lines; the rate is 100 times covered over valid, rounded to two decimals. Baseline has one file; final has three. Final rate must be at least the baseline rate. Per-method rows for `HandleToggleClickAsync`, `CompletePrime`, `TryInvokeSink` and `BuildNotifyFailedMessage` are read from the node of the file that contains the method: the two new methods must reach at least 90.00 (CLAUDE.md new-code target) and the two changed methods must have no more uncovered elements than at baseline. At final, each of the three files must have at least one class node; otherwise `PARTIAL CLASS ATTRIBUTION UNSUPPORTED`: stop and report, because the per-file figures would then be unmeasurable. +- **D-10 No commits.** This plan creates no commit and stages nothing. Every footprint gate compares the working tree against BASE-SHA with `git diff --name-only BASE-SHA` paired with `git status --porcelain --untracked-files=all` in the same task. `.claude/agent-memory/` paths are ambient state of other sessions, are never staged, and are admitted by the footprint gate. Committing is the orchestrator's step after the reduced audit. +- **D-11 Edit route.** Every `.cs` and `.csproj` change is made with the Edit or Write tool, never through a shell write, so the repository hooks see it. A hook denial of any Edit or Write is reported verbatim and stops that step; the executor does not retry with a rephrased edit or another tool. + +## Write Set (every file this plan creates or modifies) + +Code files (the only paths outside the feature folder this plan may change): + +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (modify: split, then fix) +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (create) +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (create) +- `TaskMaster/TaskMaster.csproj` (modify: two compile items) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (modify: the `OnNotify` harness member) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (create) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (modify: remark only, D-7a) +- `TaskMaster.Test/TaskMaster.Test.csproj` (modify: one compile item) + +Feature documents: + +- `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` (check-off edits `- [ ] ACn` to `- [x] ACn` only) +- `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md` (task check-off edits only) + +Evidence files (fixed names; the write time is the `Timestamp:` field), all under FEATURE/evidence/: + +- `baseline/`: `phase0-instructions-read.md`, `scope-and-anchor.md`, `anchor-production.md`, `anchor-test-side.md`, `bootstrap-sdk.md`, `bootstrap-tool-restore.md`, `bootstrap-nuget-restore.md`, `bootstrap-dotnet-coverage.md`, `csharpier-check-baseline.md`, `msbuild-analyzer-baseline.md`, `msbuild-nullable-baseline.md`, `coordinator-tests-baseline.md`, `coverage-baseline.md` (thirteen) +- `regression-testing/`: `split-census.md`, `split-fixture-green.md`, `sink-guard-partial-tokens.md`, `refusal-path-fail-before.md`, `refusal-path-pass-after.md` (five) +- `qa-gates/`: `production-edit-scope.md`, `test-partials-unchanged.md`, `file-line-counts.md`, `csharpier-format.md`, `csharpier-check-final.md`, `msbuild-analyzer-final.md`, `msbuild-nullable-final.md`, `coverage-final.md`, `coverage-comparison.md`, `toolchain-final-pass.md`, `footprint-scope.md`, `evidence-hygiene.md` (twelve) +- `other/`: `implementation-handoff.md`, `ac-status-summary.md`, `reduced-audit-handoff.md` (three) +- Preparation-phase record (committed by the preparation run before execution; not written, modified or deleted by any task of this plan): exactly one file `other/preflight-clearance..md`. + +Files this plan must not touch: every other file under `TaskMaster/` and `TaskMaster.Test/` (in particular `TaskMaster/Ribbon/RibbonController.EngineCommands.cs`, `TaskMaster/Ribbon/RibbonCommandBoundary.cs`, `TaskMaster/Ribbon/EngineTogglePressedStateCache.cs` and the partials `.PrimeFaultOrdering.cs`, `.PrimeRegistration.cs`, `.ThrowingSink.cs`, `.RepeatFaultSuppression.cs`), every `packages.config`, every file under `scripts/`, `.claude/`, `config/` and `artifacts/`, `docs/features/potential/`, `TaskMaster.runsettings`, `coverage.config`, `.editorconfig`, `.gitignore` and `.csharpierignore`. No raw trx, raw Cobertura document or msbuild log is copied into the feature folder under any name; raw documents stay under the git-ignored `coverage/` directory. + +## Delivered source (the executor writes these texts; CSharpier output wins on layout, and every gate reads whitespace-normalised text) + +Indentation rule: every block below is shown with four leading spaces of Markdown indent on each line; remove exactly those four spaces on every line when writing. Base line numbers refer to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` at BASE-SHA (fact 2), which P0-T4 proves equal to the working file before Phase 1. + +**S1 — `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (create).** Content: the header block, then base lines 47 to 51 verbatim, one blank line, base lines 437 to 494 verbatim, then the footer block. + +Header: + + using System; + using System.Globalization; + + namespace TaskMaster + { + internal sealed partial class EngineToggleStateCoordinator + { + +Footer: + + } + } + +**S2 — `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (create).** Content: the header block, then base lines 257 to 435 verbatim, then the S1 footer block. + +Header: + + using System; + using System.Threading; + using System.Threading.Tasks; + using UtilitiesCS; + + namespace TaskMaster + { + internal sealed partial class EngineToggleStateCoordinator + { + +**S3 — `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (modify).** Remove base lines 3 (`using System.Globalization;`) and 4 (`using System.Threading;`); replace line 45 with ` internal sealed partial class EngineToggleStateCoordinator`; remove base lines 47 to 52 (the constant block and the blank line after it); remove base lines 256 to 494 (the blank line after `ExecuteToggleAsync`, the moved prime members, the blank line 436 and the moved message members). The result is base lines 1, 2, 5 to 44, the new line 45, 46, 53 to 255, 495 and 496: 249 lines. + +**F1 — constructor parameter docs (main file).** Replace the `enginesAccessor` element (base 95 to 99), the `notifyUnavailable` element (base 104 to 107) and the `logError` element (base 108 to 111) with, respectively: + + /// + /// Supplies the current engines container. Must not be null, and must not throw: its + /// result is read outside any guard by and by the refusal check + /// of , so an exception it raised would escape both. + /// It is expected to return null before the ribbon controller has been given its globals, + /// which this type treats as "state unknown" rather than as an error. + /// + + /// + /// Receives exactly one message per toggle click refused because the engines are not + /// available. Presentation is the sink's concern. Must not be null. The call is guarded + /// (issue #964): an exception it throws is reported once through + /// and is not rethrown. + /// + + /// + /// Receives an observed prime fault, toggle fault or notification failure as a message + /// plus the exception. Must not be null. The call is guarded: an exception it throws is + /// discarded, because no further reporting channel remains. + /// + +**F2 — `GetPressed` returns (main file).** Replace base 134 to 138 with: + + /// + /// The cached activation state, or when the key is null, + /// whitespace, unmapped, or has never been primed. This method performs a dictionary read + /// only: it never awaits, never blocks, and never throws while the engines accessor + /// honours its non-throwing precondition. + /// + +**F3 — `HandleToggleClickAsync` (main file).** Replace the summary (base 162 to 165) with SUMMARY-HTC, the remarks (base 171 to 181) with REMARKS-HTC, and the method body (base 183 to 205, the opening brace through the closing brace) with BODY-HTC. + +SUMMARY-HTC: + + /// + /// The toggle-click boundary: the only catch clause in this type that observes an + /// engine fault. Every sink call on this path goes through , + /// which holds the only other catch clause. + /// + +REMARKS-HTC: + + /// + /// When the engines are not available the click is refused with exactly one + /// notifyUnavailable message and no engine member is invoked. That notification is + /// guarded (issue #964): if the sink throws, its exception is reported once through + /// logError. Otherwise runs inside a single + /// boundary try/catch: a fault is reported through logError, is not + /// rethrown, and does not invalidate. Every logError call is itself guarded + /// (issue #947): it is the last reporting channel, so a failure inside it has nowhere + /// else to go and is discarded deliberately, following + /// RibbonCommandBoundary.SafeLog. This method therefore never throws on either + /// path, even when both sinks throw, provided the engines accessor honours its + /// non-throwing precondition, because its caller is an async void Office handler + /// whose faults would otherwise become unobserved. + /// + +BODY-HTC: + + { + if (_enginesAccessor() is null) + { + if ( + !TryInvokeSink( + () => _notifyUnavailable(BuildUnavailableMessage(engineName)), + out var notifyFailure + ) + ) + { + _ = TryInvokeSink( + () => _logError(BuildNotifyFailedMessage(engineName), notifyFailure), + out _ + ); + } + + return; + } + + try + { + await ExecuteToggleAsync(engineName).ConfigureAwait(false); + } + catch (Exception ex) + { + _ = TryInvokeSink(() => _logError(BuildToggleFailedMessage(engineName), ex), out _); + } + } + +**F4 — `TryInvokeSink` (main file, new).** Insert directly after the closing brace of `ExecuteToggleAsync` (the last method of the main file), preceded by one blank line: + + /// + /// Invokes one injected sink and contains any exception it throws (issues #947 and #964). + /// This holds the only catch clause in this type that intercepts a sink failure; + /// every notifyUnavailable and logError call goes through it. + /// + /// The sink invocation, with its arguments already bound. + /// + /// The exception the sink threw, or when the sink returned + /// normally. + /// + /// + /// when the sink returned normally; when + /// it threw. The exception is never rethrown. + /// + /// + /// The caller decides what happens to a contained failure, following + /// RibbonCommandBoundary.ReportFailure: the refusal path of + /// forwards a notification failure to the log sink, + /// and every log-sink caller discards a log failure because no further channel remains. + /// records a reported fault kind only when this method + /// returns , so a sink that throws leaves the report owed + /// (issue #948). + /// + private static bool TryInvokeSink(Action sinkCall, out Exception sinkFailure) + { + try + { + sinkCall(); + sinkFailure = null; + return true; + } + catch (Exception ex) + { + sinkFailure = ex; + return false; + } + } + +**F5 — `GetPrimeTask` documentation (Prime file).** Replace the lines from the `/// ` directly above the line containing `The in-flight` through the `/// ` directly above `internal Task GetPrimeTask(string engineName)` with: + + /// + /// The registration marker for an engine key, exposed so tests can await the outcome of + /// its prime deterministically instead of polling or sleeping. The marker is not the + /// prime task itself: it is registered before the prime starts and is completed only after + /// the prime outcome has been observed and, on a fault or cancellation, reported. + /// + /// The engine key; ordinal, case-sensitive. + /// + /// The registered marker, or when no marker is registered + /// for the key. The marker never faults or cancels: a prime fault is observed by + /// and reported through logError, and the marker is + /// completed in a finally after that observation. For a key whose prime did not + /// run to completion, the marker is cleared only after that report has returned or + /// thrown, so a caller that receives can rely on the + /// report having been attempted or deliberately suppressed as a repeat of a kind already + /// reported. + /// + +**F6 — `StartObservedPrime` remarks (Prime file).** Replace the nine remark text lines from the line containing `The observer is a continuation rather than a` through the line containing `of the sink, the discarded continuation has no remaining throw source of its own.` with: + + /// The observer is a continuation rather than a catch clause. The two + /// catch clauses in this type are the click boundary in + /// and the single sink guard in + /// . Reading inside + /// marks the fault observed, so no unobserved task remains. + /// The continuation task itself is discarded; the value a test awaits is the marker, + /// which the continuation completes only through SetResult in a finally + /// after exits, so it never faults or cancels. Because + /// routes its sink call through , + /// the discarded continuation has no remaining throw source of its own. + +**F7 — `CompletePrime` (Prime file).** Three replacements inside the `CompletePrime` documentation and body. + +F7a: replace the summary text line containing `sink failure is contained here, and only then is the marker cleared for a later re-prime.` with the two lines: + + /// sink failure is contained by , and only then is the marker + /// cleared for a later re-prime. + +F7b: replace the second remarks paragraph text (the seven lines from the line containing `The sink call is guarded (issue #947). The sink is the last reporting channel of this` through the line containing `completes rather than faulting.`) with the first block below, and the third paragraph text (the four lines from the line containing `Repeat suppression (issue #948)` through the line containing `that record before the sink, or into a catch or finally arm, suppresses it for the session.`) with the second block: + + /// The sink call is guarded (issue #947) through , the guard + /// this type uses at every sink call site (issue #964). The sink is the last reporting + /// channel of this type, so a failure inside it has nowhere else to go; letting it escape + /// skipped the clear below, which left a stale marker that blocked every later re-prime, + /// and faulted the discarded continuation unobserved. With the sink contained, the + /// continuation in has no remaining throw source of its + /// own, so it completes rather than faulting. + + /// Repeat suppression (issue #948): each pair of engine key and base-exception type is + /// reported once, then recorded in by the only + /// statement of the branch taken when reports that the sink + /// returned normally, so a sink that throws leaves the report owed. Moving that record + /// before the sink call, or out of that branch, suppresses it for the session. + +F7c: replace the guarded report (from the line `if (!_reportedPrimeFaults.ContainsKey(reportKey))` through its closing brace, base 421 to 432) with: + + if (!_reportedPrimeFaults.ContainsKey(reportKey)) + { + if ( + TryInvokeSink( + () => _logError(BuildPrimeFailedMessage(engineName), failure), + out _ + ) + ) + { + _reportedPrimeFaults[reportKey] = 0; + } + } + +The `// Report-then-clear is load-bearing:` comment and the final `_primeTasks.TryRemove(engineName, out _);` are unchanged. + +**F8 — `BuildNotifyFailedMessage` (Messages file, new).** Insert directly after the closing brace of `BuildToggleFailedMessage`, preceded by one blank line: + + /// + /// The message logged when the notification for a refused toggle click throws. + /// + private static string BuildNotifyFailedMessage(string engineName) + { + return string.Format( + CultureInfo.CurrentCulture, + "Notifying that the engine '{0}' is not available failed, so the refused toggle " + + "click was not surfaced to the user.", + RenderEngineName(engineName) + ); + } + +**T1 — `Harness` notification hook (`TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs`).** Replace the line `message => Notifications.Add(message),` (base 414) with the first block, and insert the second block (the property, then one blank line) directly after the line `internal Action OnLogError { get; set; }` and the blank line that follows it: + + message => + { + Notifications.Add(message); + OnNotify?.Invoke(message); + }, + + /// + /// An optional extra observer invoked from inside the notification sink, immediately + /// after the message has been appended to , so a throwing + /// hook both records the attempt and models a throwing notification sink. + /// + internal Action OnNotify { get; set; } + +**T2 — `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (create).** + + using System; + using System.Threading.Tasks; + using FluentAssertions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + namespace TaskMaster.Test.Ribbon + { + /// + /// Regression tests for issue #964: on the refusal path of HandleToggleClickAsync, + /// taken when the engines accessor yields null, a notifyUnavailable sink that throws + /// must not escape into the async void Office handler, and its exception must be + /// reported once through logError; plus a guard for the issue #948 record placement + /// now that every sink call goes through one shared guard. A further partial of the + /// coordinator fixture, so the private Harness and LoggedError types and the + /// fixture constants are reused. The harness invokes OnNotify and OnLogError + /// after it has recorded the call, so a throwing hook both records the attempt and models a + /// throwing sink. No test sleeps, polls, reads the clock or touches the filesystem. + /// + public partial class EngineToggleStateCoordinatorTests + { + #region Issue #964 — a throwing notification sink on the refusal path + + /// + /// Regression for issue #964 and the test that carries the fail-before obligation. + /// Invariant: with the engines unavailable, a throwing notification sink does not escape + /// the click handler. Without the fix the exception escapes the unguarded notification + /// call, so the awaited call faults with it. + /// + [TestMethod] + public async Task HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow() + { + // Arrange: the pre-SetGlobals window, with a notification sink that throws. + var harness = new Harness { EnginesAvailable = false }; + harness.OnNotify = _ => throw new InvalidOperationException("notify sink failed"); + + // Act + Func act = () => harness.Coordinator.HandleToggleClickAsync(SpamEngine); + + // Assert + await act.Should() + .NotThrowAsync("a throwing notification sink must not escape the refusal path"); + } + + /// + /// Regression for issue #964, the reporting guarantee: the notification is attempted + /// once, its exception reaches the log sink once and unchanged, and the refused click + /// still touches no engine member and invalidates no control. Without the fix the + /// exception escapes, so the test method throws before any assertion runs. + /// + [TestMethod] + public async Task HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing() + { + // Arrange + var harness = new Harness { EnginesAvailable = false }; + var notifyFailure = new InvalidOperationException("notify sink failed"); + harness.OnNotify = _ => throw notifyFailure; + + // Act + await harness.Coordinator.HandleToggleClickAsync(SpamEngine); + + // Assert + harness + .Notifications.Should() + .ContainSingle("the notification sink is attempted exactly once"); + harness + .Errors.Should() + .ContainSingle("a notification failure is reported once through the log sink"); + harness + .Errors[0] + .Exception.Should() + .BeSameAs(notifyFailure, "the log sink receives the notification failure unchanged"); + harness.Errors[0].Message.Should().Contain(SpamEngine); + harness.Engines.VerifyNoOtherCalls(); + harness.Invalidations.Should().BeEmpty("a refused click changes no state to display"); + } + + /// + /// Regression for issue #964, both sinks failing: when the log sink also throws while it + /// reports the notification failure, the click handler still completes without throwing, + /// because no further reporting channel remains. Without the fix the notification + /// exception escapes first. + /// + [TestMethod] + public async Task HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow() + { + // Arrange + var harness = new Harness { EnginesAvailable = false }; + var notifyFailure = new InvalidOperationException("notify sink failed"); + harness.OnNotify = _ => throw notifyFailure; + harness.OnLogError = (_, _) => throw new InvalidOperationException("log sink failed"); + + // Act + Func act = () => harness.Coordinator.HandleToggleClickAsync(SpamEngine); + + // Assert + await act.Should().NotThrowAsync("the refusal path contains a failure of both sinks"); + harness.Notifications.Should().ContainSingle("the notification is attempted once"); + harness + .Errors.Should() + .ContainSingle("the log sink is attempted once before it throws"); + harness + .Errors[0] + .Exception.Should() + .BeSameAs(notifyFailure, "the log sink receives the notification failure unchanged"); + } + + #endregion Issue #964 — a throwing notification sink on the refusal path + + #region Issue #964 — the issue #948 record placement under the shared guard + + /// + /// Guard for the issue #948 invariant now that the prime-fault sink call goes through the + /// shared guard: a log sink that throws while a faulted prime is reported leaves that + /// failure kind unrecorded, so the next fault of the same kind is reported again. Passes + /// before and after the issue #964 change; it fails if the record moves ahead of the sink + /// call or out of the branch taken when the sink returned normally. + /// + [TestMethod] + public async Task GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain() + { + // Arrange: two faulted primes of one kind; the sink throws on the first report only. + var harness = new Harness(); + var firstProbe = new TaskCompletionSource(); + var secondProbe = new TaskCompletionSource(); + harness + .Engines.SetupSequence(x => x.EngineActiveAsync(SpamEngine)) + .Returns(firstProbe.Task) + .Returns(secondProbe.Task); + var reports = 0; + harness.OnLogError = (_, _) => + { + reports++; + if (reports == 1) + { + throw new InvalidOperationException("log sink failed"); + } + }; + harness.Coordinator.GetPressed(SpamEngine); + var firstPrime = harness.Coordinator.GetPrimeTask(SpamEngine); + + // Act + firstProbe.SetException(new InvalidOperationException("configuration load failed")); + await firstPrime; + harness.Coordinator.GetPressed(SpamEngine); + var secondPrime = harness.Coordinator.GetPrimeTask(SpamEngine); + secondProbe.SetException(new InvalidOperationException("configuration load failed")); + await secondPrime; + + // Assert + secondPrime.Should().NotBeSameAs(firstPrime, "the later read registered a new prime"); + harness + .Errors.Should() + .HaveCount( + 2, + "a report the throwing sink did not accept is still owed, so the repeat is reported" + ); + harness.Errors[1].Message.Should().Contain(SpamEngine); + } + + #endregion Issue #964 — the issue #948 record placement under the shared guard + } + } + +**T3 — `.Race.cs` remark (related defect D-7a).** Replace the six remark text lines from the line containing `Assertion order is load-bearing. The harness engines mock is strict and this test` through the line containing `deterministic.` with: + + /// Assertion order is load-bearing. The harness engines mock is strict and this test + /// supplies one setup, so the re-prime triggered by the second read re-enters that same + /// canceled task. Since issue #948 that second cancellation is a repeat of a kind already + /// reported and is not logged, but the single-error assertion is still made before the + /// re-prime so the test does not depend on the suppression rule. The marker-cleared + /// conclusion is drawn from prime-handle identity, which is deterministic. + +**C1 — `TaskMaster/TaskMaster.csproj`.** Insert directly after the line `` the two lines below; after the four Markdown-indent spaces are removed each line keeps four leading spaces, matching its neighbours: + + + + +**C2 — `TaskMaster.Test/TaskMaster.Test.csproj`.** Insert directly after the line `` the line below (four leading spaces after the Markdown indent is removed): + + + +## Name lists + +- `NEW-NAMES-964`: `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow`, `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing`, `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`, `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain`. The first three are `FAIL-BEFORE-NAMES`; the fourth is `GUARD-NAME`. +- `INVARIANT-NAMES` (existing tests AC4 names): `GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns`, `GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime`, `GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime`, `GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged`, `GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime`, `GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime`, `GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared`, `HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport`, `GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly`, `GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly`, `HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing`. +- `NAMES-ALL` is `NEW-NAMES-964` followed by `INVARIANT-NAMES`, written as a PowerShell list of double-quoted strings when substituted into `CMD-VSTEST`. + +## Execution conventions + +- **Paths.** `WORKTREE` denotes the absolute item worktree path from the delegation prompt; it is substituted into each payload's first line and is never written into an artifact. Every artifact records repository-relative paths; absolute paths in transcribed tool output are replaced by `REDACTED-PATH`. +- **Payload channel.** Each indented payload block is executed as one `pwsh -NoProfile -Command ''` Bash invocation (the only permitted shell form besides `git`): outer single quotes, inner double quotes only; a double quote needed inside a payload string is built from `[char]34` and an apostrophe from `[char]39`; no payload carries a backslash-escaped double quote, a literal apostrophe or a backtick. No payload combines git with the substrings add, commit or remove, and no payload contains all of gh, pr and create, because the hook command scanners match those by case-insensitive containment. The `Command:` field of an artifact records the canonical command the payload runs, not the payload text. +- **Git commands outside payloads** run as `git -C WORKTREE `; the `Command:` field records them without `-C`. +- **Toolchain commands.** Every `dotnet`, `msbuild` and `vstest.console.exe` command named in a task runs inside one `pwsh -NoProfile -Command` payload that begins with PRELUDE, because the first token of a permitted Bash command must be `git`, `pwsh` or `poetry`. A task that names only the tool command (for example `dotnet tool run csharpier check` over three paths) means that command wrapped as `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; ; "EXIT: $LASTEXITCODE"'`. +- **Grep tool patterns.** Every pattern this plan gives to the Grep tool is a regular expression with its metacharacters escaped as written: `\(`, `\)`, `\[`, `\]`, `\.`, `\?`, and a literal backslash as `\x5C`. A pattern written without any metacharacter is a plain phrase. The Grep tool counts matching lines. +- **PRELUDE** (the first two lines of every payload): + + Set-Location -LiteralPath "WORKTREE" + [Environment]::CurrentDirectory = (Get-Location).Path + +- **TOOLS** (the lines of every build and test payload after PRELUDE): + + $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" + $msbuild = & $vswhere -latest -products * -find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1 + $vstest = & $vswhere -latest -products * -find "Common7\IDE\Extensions\TestPlatform\vstest.console.exe" | Select-Object -First 1 + $sln = Join-Path (Get-Location).Path "TaskMaster.sln" + New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null + +- **Exit codes.** `EXIT_CODE:` records the printed exit value of the principal command. A deliberately or admissibly failing run carries `ExpectedExitCode:` equal to the observed non-zero value in its own artifact. +- **Long runs.** `CMD-COVERAGE-RUNNER` is started as a background process with output redirected to `coverage\logs\STAGE-964.payload.log`; completion is detected by the final line `PAYLOAD-COMPLETE`. Before every vstest or coverage run the executor runs `pwsh -NoProfile -Command '"STRAY_TEST_PROCESSES: " + @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -like "vstest*" -or $_.ProcessName -like "testhost*" -or $_.ProcessName -like "dotnet-coverage*" }).Count'` and proceeds only on `STRAY_TEST_PROCESSES: 0`. A coverage run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report; it is never re-run with a different route. +- **Stop rule.** A named stop condition halts the plan; the executor reports the artifact and the failing values and does not work around it. + +## Command reference + +**CMD-REBUILD** (`GATEARGS` is `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` or `/p:TreatWarningsAsErrors=true`; `TASKID` substituted; `Command:` records `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS` resolved through vswhere with the worktree solution by absolute path; never `/t:Build`, never `/p:Nullable=enable`): + + PRELUDE + TOOLS + $log = "coverage\logs\TASKID.msbuild.log" + if (Test-Path -LiteralPath $log) { Remove-Item -LiteralPath $log -Force } + $global:LASTEXITCODE = 0 + & $msbuild $sln /t:Rebuild /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS "/flp:LogFile=$log;Verbosity=normal" | Out-Null + Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE) + $lines = Get-Content -LiteralPath $log -Encoding UTF8 + Write-Output ("ERRORS: " + (($lines | Select-String -Pattern "^\s*(\d+) Error\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + Write-Output ("WARNINGS: " + (($lines | Select-String -Pattern "^\s*(\d+) Warning\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + Write-Output ("CSC_OUT_TASKMASTER: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.dll") }).Count) + Write-Output ("CSC_OUT_TASKMASTER_TEST: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.Test.dll") }).Count) + Write-Output ("WRITESET_DIAGNOSTIC_LINES: " + @($lines | Where-Object { ($_.Contains("EngineToggleStateCoordinator")) -and ($_ -match "(error|warning) [A-Z]+\d+") }).Count) + Write-Output ("TEST_DLL_EXISTS: " + (Test-Path -LiteralPath "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll")) + +`ERRORS:` is read from the summary line, so `0 Error(s)` is never matched inside a larger count. The two `CSC_OUT_` counts are the observation that the compiler ran for the two Write Set projects. `WRITESET_DIAGNOSTIC_LINES` counts every error or warning line naming any Write Set source file, because every one contains `EngineToggleStateCoordinator`. + +**CMD-BUILD** (incremental build so a scoped test run observes a fresh assembly; `TASKID` substituted; `Command:` records `msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU"`; never used as a toolchain gate): + + PRELUDE + TOOLS + $log = "coverage\logs\TASKID.msbuild.log" + if (Test-Path -LiteralPath $log) { Remove-Item -LiteralPath $log -Force } + $before = (Get-Item -LiteralPath "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll" -ErrorAction SilentlyContinue).LastWriteTimeUtc + $global:LASTEXITCODE = 0 + & $msbuild $sln /t:Build /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" "/flp:LogFile=$log;Verbosity=normal" | Out-Null + Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE) + $lines = Get-Content -LiteralPath $log -Encoding UTF8 + Write-Output ("ERRORS: " + (($lines | Select-String -Pattern "^\s*(\d+) Error\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + $after = (Get-Item -LiteralPath "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll").LastWriteTimeUtc + Write-Output ("TEST_DLL_ADVANCED: " + ($null -eq $before -or $after -gt $before)) + Write-Output ("CSC_OUT_TASKMASTER: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.dll") }).Count) + Write-Output ("CSC_OUT_TASKMASTER_TEST: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.Test.dll") }).Count) + +**CMD-VSTEST** (coordinator fixture run; `TASKID` and `NAMES` substituted; `Command:` records `vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\TASKID" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"` resolved through vswhere; a run whose filter matches zero tests is a failure): + + PRELUDE + TOOLS + $results = "coverage\test-results\964\TASKID" + if (Test-Path -LiteralPath $results) { Remove-Item -LiteralPath $results -Recurse -Force } + $names = @(NAMES) + $global:LASTEXITCODE = 0 + & $vstest "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll" /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:$results" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" 2>&1 | Tee-Object -FilePath "coverage\logs\TASKID.vstest.log" | Out-Null + Write-Output ("VSTEST_EXIT_CODE: " + $LASTEXITCODE) + $trxPath = Join-Path $results "TASKID.trx" + Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath $trxPath)) + Write-Output ("SEQUENCE_FILES: " + @(Get-ChildItem -LiteralPath $results -Recurse -Filter "Sequence_*.xml" -ErrorAction SilentlyContinue).Count) + if (-not (Test-Path -LiteralPath $trxPath)) { exit 3 } + [xml]$trx = Get-Content -LiteralPath $trxPath -Raw -Encoding UTF8 + $ns = New-Object System.Xml.XmlNamespaceManager($trx.NameTable) + $ns.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010") + $counters = $trx.SelectSingleNode("//t:ResultSummary/t:Counters", $ns) + Write-Output ("COUNTERS total=" + $counters.GetAttribute("total") + " executed=" + $counters.GetAttribute("executed") + " passed=" + $counters.GetAttribute("passed") + " failed=" + $counters.GetAttribute("failed")) + $all = @($trx.SelectNodes("//t:UnitTestResult", $ns)) + foreach ($r in $all) { if ($names -contains $r.GetAttribute("testName")) { Write-Output ("RESULT " + $r.GetAttribute("testName") + " = " + $r.GetAttribute("outcome")) } } + foreach ($r in $all) { if ($r.GetAttribute("outcome") -eq "Failed") { Write-Output ("FAILED " + $r.GetAttribute("testName")); $msg = $r.SelectSingleNode("t:Output/t:ErrorInfo/t:Message", $ns); Write-Output ("MESSAGE " + $r.GetAttribute("testName") + " :: " + $(if ($msg) { $msg.InnerText.Replace([string][char]13, " ").Replace([string][char]10, " / ") } else { "(no message)" })) } } + +The artifact transcribes the `COUNTERS`, `RESULT`, `FAILED` and `MESSAGE` lines; the trx stays under the ignored coverage directory. + +**CMD-COVERAGE-RUNNER** (CLAUDE.md step 4; `STAGE` is `baseline` or `final`; `Command:` records `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1` invoked by absolute script path from the worktree directory): + + PRELUDE + New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null + foreach ($f in @("coverage\coverage.cobertura.xml", "coverage\coverage.cobertura.jacoco.xml", "coverage\test-results\mstest-coverage-run.trx", "coverage\test-results\mstest-coverage-run.summary.txt", "coverage\STAGE-964.cobertura.xml", "coverage\STAGE-964.trx")) { if (Test-Path -LiteralPath $f) { Remove-Item -LiteralPath $f -Force } } + $script = Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.ps1" + $global:LASTEXITCODE = 0 + & pwsh -NoProfile -File $script 2>&1 | Tee-Object -FilePath "coverage\logs\STAGE-964.runner.log" | Out-Null + Write-Output ("RUNNER_EXIT_CODE: " + $LASTEXITCODE) + $log = Get-Content -LiteralPath "coverage\logs\STAGE-964.runner.log" -Raw -Encoding UTF8 + Write-Output ("DISCOVERED_LINE: " + [regex]::Match($log, "Discovered \d+ test assemblies\.").Value) + Write-Output ("FIRST_PARTY_LINE: " + [regex]::Match($log, "First-party coverage: [^\r\n]*").Value) + Write-Output ("THRESHOLD_MESSAGE: " + [regex]::Match($log, "Cobertura (line|branch) coverage [^\r\n]*threshold[^\r\n]*").Value) + Write-Output ("COLLECT_FAILURE_MESSAGE: " + [regex]::Match($log, "MSTest with coverage failed with exit code \d+").Value) + Write-Output ("DOCUMENT_PRESENT: " + (Test-Path -LiteralPath "coverage\coverage.cobertura.xml")) + Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.trx")) + Write-Output ("SUMMARY_FILE_PRESENT: " + (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.summary.txt")) + if (Test-Path -LiteralPath "coverage\coverage.cobertura.xml") { Copy-Item -LiteralPath "coverage\coverage.cobertura.xml" -Destination "coverage\STAGE-964.cobertura.xml" -Force } + if (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.trx") { Copy-Item -LiteralPath "coverage\test-results\mstest-coverage-run.trx" -Destination "coverage\STAGE-964.trx" -Force } + Write-Output "PAYLOAD-COMPLETE" + +The stale-output removal makes every `_PRESENT` value an observation of this run. Lines of the runner log that carry absolute paths stay in the ignored log; only the named values are transcribed. + +**CMD-COVERAGE-POST** (`STAGE` substituted; `RAW` is `True` when the runner printed a non-empty `COLLECT_FAILURE_MESSAGE:`, otherwise `False`, because a completed runner run has already post-processed the document in place): + + PRELUDE + . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1") + . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTest.TrxSummary.ps1") + $ErrorActionPreference = "Continue" + $repo = (Get-Location).Path + $summary = Get-TrxRunSummary -TrxContent (Get-Content -LiteralPath "coverage\STAGE-964.trx" -Raw -Encoding UTF8) + Write-Output "SUMMARY-BEGIN" + Write-Output (Format-TrxRunSummary -Summary $summary) + Write-Output "SUMMARY-END" + Write-Output ("FAILED-SET: " + (@($summary.FailedTestName) -join ", ")) + [xml]$trxXml = Get-Content -LiteralPath "coverage\STAGE-964.trx" -Raw -Encoding UTF8 + $tns = New-Object System.Xml.XmlNamespaceManager($trxXml.NameTable) + $tns.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010") + $defs = @{} + foreach ($u in @($trxXml.SelectNodes("//t:TestDefinitions/t:UnitTest", $tns))) { $tm = $u.SelectSingleNode("t:TestMethod", $tns); $defs[$u.GetAttribute("id")] = $tm.GetAttribute("className").Split([char]44)[0].Trim() + "." + $tm.GetAttribute("name") } + Write-Output ("TEST-DEFINITIONS: " + $defs.Count) + $fqn = @(@($trxXml.SelectNodes("//t:Results/t:UnitTestResult", $tns)) | Where-Object { $_.GetAttribute("outcome") -eq "Failed" } | ForEach-Object { $id = $_.GetAttribute("testId"); if ($defs.ContainsKey($id)) { $defs[$id] } else { "UNRESOLVED:" + $id } } | Sort-Object -Unique -CaseSensitive) + Write-Output ("FAILED-FQN-COUNT: " + $fqn.Count) + foreach ($n in $fqn) { Write-Output ("FAILED-FQN " + $n) } + $doc = Get-Content -LiteralPath "coverage\STAGE-964.cobertura.xml" -Raw -Encoding UTF8 + if ("RAW" -eq "True") { $doc = ConvertTo-KoverageCoberturaXml -XmlContent $doc -RepoRoot $repo; Set-Content -LiteralPath "coverage\STAGE-964.cobertura.xml" -Value $doc -Encoding UTF8 -NoNewline } + try { Assert-CoberturaLineCoverageThreshold -CoberturaXml $doc; Write-Output "LINE-FLOOR: MET" } catch { Write-Output ("LINE-FLOOR: NOT MET " + $_.Exception.Message) } + try { Assert-CoberturaBranchCoverageThreshold -CoberturaXml $doc; Write-Output "BRANCH-FLOOR: MET" } catch { Write-Output ("BRANCH-FLOOR: NOT MET " + $_.Exception.Message) } + Write-Output (Get-CoberturaFirstPartyCoverageReport -CoberturaXml $doc) + [xml]$xml = $doc + $root = $xml.SelectSingleNode("/coverage") + Write-Output ("ROOT line-rate=" + $root.GetAttribute("line-rate") + " branch-rate=" + $root.GetAttribute("branch-rate") + " lines-covered=" + $root.GetAttribute("lines-covered") + " lines-valid=" + $root.GetAttribute("lines-valid")) + $projection = ConvertTo-JacocoPackageProjection -XmlDocument $xml + Assert-JacocoProjectionReconciliation -XmlDocument $xml -ProjectionXml $projection + Write-Output "PROJECTION-BEGIN" + Write-Output $projection + Write-Output "PROJECTION-END" + $srcFiles = @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs") + $nodesTotal = 0; $cv = 0; $vl = 0; $cb = 0; $vb = 0 + foreach ($f in $srcFiles) { + $norm = $f.Replace([string][char]92, "/") + $nodes = @($xml.SelectNodes("//class[@filename]") | Where-Object { $_.GetAttribute("filename").Replace([string][char]92, "/").EndsWith($norm) }) + $fc = 0; $fv = 0 + foreach ($c in $nodes) { $s = Get-CoberturaClassLineSummary -ClassNode $c; $fc += $s.CoveredLines; $fv += $s.TotalLines; $cb += $s.CoveredBranches; $vb += $s.TotalBranches } + $nodesTotal += $nodes.Count; $cv += $fc; $vl += $fv + Write-Output ("COORD-FILE " + $norm + " nodes=" + $nodes.Count + " covered=" + $fc + " valid=" + $fv) + } + Write-Output ("COORD-CLASS-NODES: " + $nodesTotal) + Write-Output ("COORD-LINES covered=" + $cv + " valid=" + $vl) + Write-Output ("COORD-BRANCHES covered=" + $cb + " valid=" + $vb) + Write-Output ("COORD-LINE-RATE: " + $(if ($vl -gt 0) { [math]::Round(100.0 * $cv / $vl, 2) } else { "NA" })) + foreach ($m in @("HandleToggleClickAsync", "CompletePrime", "TryInvokeSink", "BuildNotifyFailedMessage")) { + $found = $false + foreach ($f in $srcFiles) { + if ($found -or -not (Test-Path -LiteralPath $f)) { continue } + $src = @(Get-Content -LiteralPath $f -Encoding UTF8) + $start = 0; for ($i = 0; $i -lt $src.Count; $i++) { if ($src[$i] -match ("^\s{8}(private|internal) (static )?(async )?\w+ " + $m + "\(")) { $start = $i + 1; break } } + if ($start -eq 0) { continue } + $end = 0; for ($i = $start; $i -lt $src.Count; $i++) { if ($src[$i].TrimEnd() -eq " }") { $end = $i + 1; break } } + $norm = $f.Replace([string][char]92, "/") + $nodes = @($xml.SelectNodes("//class[@filename]") | Where-Object { $_.GetAttribute("filename").Replace([string][char]92, "/").EndsWith($norm) }) + $map = @{} + foreach ($c in $nodes) { $s = Get-CoberturaClassLineSummary -ClassNode $c; foreach ($k in $s.LineMap.Keys) { if (-not $map.ContainsKey($k) -or $s.LineMap[$k].Hits -gt $map[$k]) { $map[$k] = $s.LineMap[$k].Hits } } } + $inSpan = @($map.Keys | Where-Object { $_ -ge $start -and $_ -le $end } | Sort-Object) + $cov = @($inSpan | Where-Object { $map[$_] -ge 1 }).Count + $rate = if ($inSpan.Count -gt 0) { [math]::Round(100.0 * $cov / $inSpan.Count, 2) } else { "NA" } + Write-Output ("METHOD " + $m + " file=" + $norm + " span=" + $start + "-" + $end + " nodes=" + $nodes.Count + " elements=" + $inSpan.Count + " covered=" + $cov + " uncovered=" + ($inSpan.Count - $cov) + " rate=" + $rate) + foreach ($n in $inSpan) { Write-Output ("METHOD-LINE " + $m + " " + $n + " hits=" + $map[$n]) } + $found = $true + } + if (-not $found) { Write-Output ("METHOD " + $m + " ABSENT") } + } + +At baseline only the main file exists, so `TryInvokeSink` and `BuildNotifyFailedMessage` read `ABSENT` and the Prime and Messages `COORD-FILE` rows read `nodes=0`; those are the expected baseline rows. The summary block and the projection are the two CLAUDE.md committed forms; every other line is a figure, not a document. `FAILED-SET:` (short method names from `Get-TrxRunSummary`, fact 14) is recorded as an observation only; every pre-existing-failure comparison reads the `TEST-DEFINITIONS:`, `FAILED-FQN-COUNT:` and `FAILED-FQN` rows, which are fully qualified names (a `TestMethod` `className` up to its first comma, a dot, and its `name`). + +**CMD-LINECOUNT** (line counts of every coordinator source file and every fixture partial present, enumerated from the directories): + + PRELUDE + $files = @(Get-ChildItem -LiteralPath "TaskMaster\Ribbon" -File -Filter "EngineToggleStateCoordinator*.cs" | Sort-Object Name | ForEach-Object { Join-Path "TaskMaster\Ribbon" $_.Name }) + @(Get-ChildItem -LiteralPath "TaskMaster.Test\Ribbon" -File -Filter "EngineToggleStateCoordinatorTests*.cs" | Sort-Object Name | ForEach-Object { Join-Path "TaskMaster.Test\Ribbon" $_.Name }) + foreach ($p in $files) { Write-Output ("LINES " + $p + " = " + @(Get-Content -LiteralPath $p -Encoding UTF8).Count) } + Write-Output ("PRODUCTION-FILES: " + @($files | Where-Object { $_.StartsWith("TaskMaster\Ribbon") }).Count) + Write-Output ("TEST-PARTIALS: " + @($files | Where-Object { $_.StartsWith("TaskMaster.Test\Ribbon") }).Count) + +**CMD-STRIPPED-COUNT** (`TOKENS` substituted; counts over the concatenated text of the coordinator source files present with every whitespace run removed, so a formatter line break cannot change a count): + + PRELUDE + $all = "" + foreach ($f in @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs")) { if (Test-Path -LiteralPath $f) { $all += (Get-Content -LiteralPath $f -Raw -Encoding UTF8) } } + $text = [regex]::Replace($all, "\s+", "") + foreach ($t in @(TOKENS)) { Write-Output ("STRIPPED [" + $t + "] = " + ([regex]::Matches($text, [regex]::Escape($t))).Count) } + +**CMD-PHRASE-COUNT** (`PHRASES` substituted; each line of the coordinator source files present is trimmed and stripped of a leading `//` or `///` marker, the lines are joined with single spaces and every whitespace run collapses to one space, so a phrase that a comment wraps across lines still counts once): + + PRELUDE + $parts = New-Object System.Collections.Generic.List[string] + foreach ($f in @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs")) { if (Test-Path -LiteralPath $f) { foreach ($l in @(Get-Content -LiteralPath $f -Encoding UTF8)) { $parts.Add([regex]::Replace($l.Trim(), "^/{2,3}\s?", "")) } } } + $text = [regex]::Replace(($parts -join " "), "\s+", " ") + foreach ($t in @(PHRASES)) { Write-Output ("PHRASE [" + $t + "] = " + ([regex]::Matches($text, [regex]::Escape($t))).Count) } + +**CMD-SPLIT-CENSUS** (ordinal multiset of non-blank trimmed lines: BASE-SHA file against the three split files): + + PRELUDE + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + $base = @(git show "94287369908cc920b21b0e3256314f988ad7d2f5:TaskMaster/Ribbon/EngineToggleStateCoordinator.cs") + $work = @() + foreach ($p in @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs")) { $work += @(Get-Content -LiteralPath $p -Encoding UTF8) } + function Get-Bag([string[]]$lines) { $bag = [System.Collections.Generic.Dictionary[string,int]]::new([System.StringComparer]::Ordinal); foreach ($l in $lines) { $t = $l.TrimStart([char]0xFEFF).Trim(); if ($t.Length -gt 0) { if ($bag.ContainsKey($t)) { $bag[$t] = $bag[$t] + 1 } else { $bag[$t] = 1 } } }; return ,$bag } + $b = Get-Bag $base; $w = Get-Bag $work + $keys = @(@($b.Keys) + @($w.Keys) | Sort-Object -Unique -CaseSensitive) + foreach ($k in $keys) { $nb = 0; $nw = 0; if ($b.ContainsKey($k)) { $nb = $b[$k] }; if ($w.ContainsKey($k)) { $nw = $w[$k] }; if ($nb -gt $nw) { Write-Output ("MISSING x" + ($nb - $nw) + " :: " + $k) }; if ($nw -gt $nb) { Write-Output ("EXTRA x" + ($nw - $nb) + " :: " + $k) } } + Write-Output ("BASE-LINES: " + $base.Count + " WORK-LINES: " + $work.Count) + +**CMD-PROTECTED-SPANS** (hashes the span of every signature in `SIGNATURES` in the BASE-SHA file and in whichever split file holds it; a span runs from the first line containing the signature through the first following line that is exactly eight spaces and a closing brace; two field declarations are hashed between explicit start and end tokens): + + PRELUDE + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + $left = @(git show "94287369908cc920b21b0e3256314f988ad7d2f5:TaskMaster/Ribbon/EngineToggleStateCoordinator.cs") + if ($left.Count -gt 0) { $left[0] = $left[0].TrimStart([char]0xFEFF) } + $work = @() + foreach ($p in @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs")) { $work += @(Get-Content -LiteralPath $p -Encoding UTF8) } + function Get-SpanHash([string[]]$lines, [string]$st, [string]$et) { $a = -1; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i].Contains($st)) { $a = $i; break } }; if ($a -lt 0) { return "ABSENT" }; $b = -1; for ($i = $a + 1; $i -lt $lines.Count; $i++) { if (($et -eq "CLOSE" -and $lines[$i].TrimEnd() -eq " }") -or ($et -ne "CLOSE" -and $lines[$i].Contains($et))) { $b = $i; break } }; if ($b -lt 0) { return "UNTERMINATED" }; $text = ((@($lines[$a..$b]) | ForEach-Object { $_.TrimEnd([char]13).TrimEnd() }) -join ([string][char]10)); return [BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($text))) } + foreach ($sig in @(SIGNATURES)) { $l = Get-SpanHash $left $sig "CLOSE"; $r = Get-SpanHash $work $sig "CLOSE"; Write-Output ("SPAN-HASH [" + $sig + "] equal=" + ($l -eq $r -and $l -ne "ABSENT" -and $l -ne "UNTERMINATED")) } + foreach ($pair in @(@("_primeTasks = new ConcurrentDictionary<", ">(StringComparer.Ordinal);"), @("(string EngineName, Type FaultType),", "_reportedPrimeFaults = new ConcurrentDictionary<(string, Type), byte>();"))) { $l = Get-SpanHash $left $pair[0] $pair[1]; $r = Get-SpanHash $work $pair[0] $pair[1]; Write-Output ("SPAN-HASH [" + $pair[0] + "] equal=" + ($l -eq $r -and $l -ne "ABSENT" -and $l -ne "UNTERMINATED")) } + +`SIGNATURES-PROTECTED`: `"internal EngineToggleStateCoordinator(", "internal bool GetPressed(string engineName)", "internal async Task ExecuteToggleAsync(string engineName)", "internal Task GetPrimeTask(string engineName)", "private void StartPrimeIfNeeded(string engineName, string controlId)", "private void StartObservedPrime(", "private async Task ApplyPrimeAsync(", "private static string RenderEngineName(string engineName)", "private static string BuildUnavailableMessage(string engineName)", "private static string BuildToggleFailedMessage(string engineName)", "private static string BuildPrimeFailedMessage(string engineName)", "private static string BuildUnmappedKeyMessage(string engineName)"`. A method span excludes the documentation above its signature, so documentation-only edits (F1, F2, F5, F6) leave these spans equal; the span of `StartPrimeIfNeeded` carries the #944 registration-before-start lines, and the span of `StartObservedPrime` carries the `finally` that completes the marker. + +**CMD-TEST-DIFF** (AC4 shape of the test-side changes against BASE-SHA): + + PRELUDE + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + foreach ($f in @("TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs", "TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs")) { + $d = @(git diff -U0 94287369908cc920b21b0e3256314f988ad7d2f5 -- $f) + $minus = @($d | Where-Object { $_.StartsWith("-") -and -not $_.StartsWith("--- ") }) + $plus = @($d | Where-Object { $_.StartsWith("+") -and -not $_.StartsWith("+++ ") }) + Write-Output ("DIFF " + $f + " minus=" + $minus.Count + " plus=" + $plus.Count) + foreach ($m in $minus) { Write-Output ("MINUS " + $f + " :: " + $m.Substring(1).Trim()) } + Write-Output ("NON-DOC-CHANGES " + $f + " = " + @(@($minus + $plus) | Where-Object { -not $_.Substring(1).Trim().StartsWith("///") }).Count) + } + foreach ($f in @("TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.PrimeFaultOrdering.cs", "TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.PrimeRegistration.cs", "TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.ThrowingSink.cs", "TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.RepeatFaultSuppression.cs")) { git diff --quiet 94287369908cc920b21b0e3256314f988ad7d2f5 -- $f; Write-Output ("UNCHANGED " + $f + " exit=" + $LASTEXITCODE) } + +**CMD-HASH** (SHA-256 of the six Write Set C# files; hashes only): + + PRELUDE + foreach ($p in @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs", "TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs", "TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs", "TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.Race.cs")) { if (Test-Path -LiteralPath $p) { Write-Output ("HASH " + $p + " = " + (Get-FileHash -Algorithm SHA256 -LiteralPath $p).Hash) } else { Write-Output ("HASH " + $p + " = ABSENT") } } + +**CMD-HYGIENE** (host-identifier sweep over every Markdown file of the feature folder; the host tokens are derived at run time and never written into the artifact): + + PRELUDE + $acct = Split-Path -Leaf $env:USERPROFILE; $machine = $env:COMPUTERNAME + $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-10-01-engine-toggle-coordinator-947-review-residuals-964" -Recurse -File -Filter "*.md") + $a = 0; $m = 0; $d = 0 + foreach ($f in $files) { $c = Get-Content -LiteralPath $f.FullName -Raw -Encoding UTF8; $fa = ([regex]::Matches($c, [regex]::Escape($acct), "IgnoreCase")).Count; $fm = ([regex]::Matches($c, [regex]::Escape($machine), "IgnoreCase")).Count; $n = $c.Replace([string][char]92, "/"); $fd = ([regex]::Matches($n, "[a-z]:/+users/+[a-z0-9_.~-]", "IgnoreCase")).Count; $a += $fa; $m += $fm; $d += $fd; if (($fa + $fm + $fd) -gt 0) { Write-Output ("HIT-FILE " + $f.Directory.Name + "/" + $f.Name + " ACCOUNT=" + $fa + " MACHINE=" + $fm + " DRIVE_USERS=" + $fd) } } + $raw = @(Get-ChildItem -LiteralPath "docs\features\active\2026-10-01-engine-toggle-coordinator-947-review-residuals-964" -Recurse -File | Where-Object { $_.Name -like "*.trx" -or $_.Name -like "*cobertura*" -or $_.Name -like "*.coverage" -or $_.Name -like "*.coveragexml" -or $_.Name -like "*.log" }).Count + Write-Output ("FILES_SCANNED=" + $files.Count + " ACCOUNT_HITS=" + $a + " MACHINE_HITS=" + $m + " DRIVE_USERS_HITS=" + $d + " RAW_DOCUMENTS=" + $raw) + +## Token and phrase sets (quoted verbatim; each is the instruction the delivered source above fulfils) + +- `TOKENS-STRUCT` (CMD-STRIPPED-COUNT; base value, then required final value): `"catch("` 3 then 2; `"catch(Exceptionex)"` 1 then 2; `"catch(Exception)"` 2 then 0; `"TryInvokeSink("` 0 then 5; `"_logError("` 2 then 3; `"_notifyUnavailable("` 1 then 1; `"TryInvokeSink(()=>_notifyUnavailable(BuildUnavailableMessage(engineName)),outvarnotifyFailure)"` 0 then 1; `"TryInvokeSink(()=>_logError(BuildNotifyFailedMessage(engineName),notifyFailure),out_)"` 0 then 1; `"TryInvokeSink(()=>_logError(BuildToggleFailedMessage(engineName),ex),out_)"` 0 then 1; `"failure),out_)){_reportedPrimeFaults[reportKey]=0;}"` 0 then 1; `"_reportedPrimeFaults[reportKey]=0;"` 1 then 1; `"privatestaticboolTryInvokeSink(ActionsinkCall,outExceptionsinkFailure)"` 0 then 1; `"privatestaticstringBuildNotifyFailedMessage(stringengineName)"` 0 then 1; `"internalsealedclassEngineToggleStateCoordinator"` 1 then 0; `"internalsealedpartialclassEngineToggleStateCoordinator"` 0 then 3; `"_primeTasks.TryRemove(engineName,out_);"` 1 then 1. +- `PHRASES-DOC` (CMD-PHRASE-COUNT; base value, then required final value): `"The in-flight"` 1 then 0; `"most recently completed"` 1 then 0; `"The prime task, or"` 1 then 0; `"The registration marker for an engine key"` 0 then 1; `"The marker is not the prime task itself"` 0 then 1; `"The registered marker, or"` 0 then 1; `"The other two are sink guards"` 1 then 0; `"which holds the only other catch clause"` 0 then 1; `"the only catch clause in this type that observes an engine fault"` 1 then 1; `"The three"` 1 then 0; `"all sit in"` 1 then 0; `"The two catch clauses in this type are the click boundary"` 0 then 1; `"also contains a failure of the sink"` 1 then 0; `"routes its sink call through"` 0 then 1; `"a sink failure is contained here"` 1 then 0; `"a sink failure is contained by"` 0 then 1; `"by the statement directly after the sink call"` 1 then 0; `"by the only statement of the branch taken when"` 0 then 1; `"never throws, even when the sink throws"` 1 then 0; `"never throws on either path, even when both sinks throw"` 0 then 1; `"honours its non-throwing precondition"` 0 then 2; `"and must not throw"` 0 then 1; `"The call is guarded (issue #964)"` 0 then 1; `"Receives an observed prime fault, toggle fault or notification failure"` 0 then 1. + +### Phase 0 — Policy Reads, Anchor and Baseline Capture + +- [ ] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/tonality.md and .claude/rules/plan-acceptance-gates.md, and record FEATURE/evidence/baseline/phase0-instructions-read.md. + - Acceptance: the artifact carries `Timestamp:`, a `Policy Order:` line naming CLAUDE.md, general-code-change.md, general-unit-test.md and csharp.md in that order, and a `Files read:` list naming all six repository-relative paths, one per line. No policy document is modified. +- [ ] [P0-T2] Read `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` in full and this plan, and record the requirements anchor and the Write Set in FEATURE/evidence/baseline/scope-and-anchor.md. + - Command: `git -C WORKTREE status --porcelain --untracked-files=all` (recorded verbatim as `INHERITED-PORCELAIN:`); the Glob tool over `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964` for `spec.md`, `user-story.md` and `research*.md`; the Glob tool over `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other` for `preflight-clearance.*.md` (recorded as `CLEARANCE-PATH:` in repository-relative form with forward-slash separators, `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance..md`; the Glob tool can return a backslash-separated or absolute path, which is converted to that form before it is recorded, because `git rev-parse HEAD:` does not resolve a backslash-separated tree path); `git -C WORKTREE rev-parse HEAD:CLEARANCE-PATH` with the recorded path substituted (recorded as `CLEARANCE-BLOB:`). + - Acceptance, all required: the artifact records that issue.md line 12 reads `- Work Mode: minor-audit`; that a heading line exactly `## Acceptance Criteria` exists; that the section holds exactly 8 lines beginning `- [ ] AC` and 0 beginning `- [x] AC` (counted with the Grep tool, patterns `^- \[ \] AC[1-8] ` and `^- \[x\] AC`); that the Glob result for the three names is `none` (a hit is `UNEXPECTED REQUIREMENTS DOCUMENT`: stop, per the minor-audit fail-closed rule); the eight code paths of the Write Set verbatim; and `INHERITED-PORCELAIN:` with every entry, each of which must lie under the feature folder, equal `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md`, or lie under `.claude/agent-memory/` (any other entry is `UNEXPECTED INHERITED CHANGE`: stop); and the preparation-phase record of the Write Set: the Glob result for `preflight-clearance.*.md` is exactly one path, recorded as `CLEARANCE-PATH:`, and `CLEARANCE-BLOB:` is the 40-hex-digit blob that `rev-parse` prints for it (zero or several paths, or a `rev-parse` failure because the record is not committed at HEAD, is `PREPARATION RECORD MISSING`: stop). +- [ ] [P0-T3] Verify the base anchor of the branch for `TaskMaster/` and `TaskMaster.Test/` against BASE-SHA and record FEATURE/evidence/baseline/anchor-production.md (this task creates the file; P0-T4 appends to it). + - Command: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse origin/main`; `git -C WORKTREE merge-base 94287369908cc920b21b0e3256314f988ad7d2f5 HEAD`; `git -C WORKTREE diff --exit-code --stat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test`. + - Acceptance, all required: the merge-base output equals `94287369908cc920b21b0e3256314f988ad7d2f5` (otherwise `BASE NOT ANCESTOR`: stop); the diff exits 0 and prints nothing (`ANCHOR-CODE-DIFF-EXIT=0`; otherwise `CODE DIFFERS FROM BASE`: stop); `HEAD:` and `ORIGIN-MAIN:` are recorded as observations, and when `ORIGIN-MAIN:` differs from BASE-SHA the artifact records `ORIGIN-MAIN MOVED` without stopping, because every anchor in this plan is the BASE-SHA literal. +- [ ] [P0-T4] Verify the split anchors and the false-before token and phrase values of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, and append them to FEATURE/evidence/baseline/anchor-production.md. + - Command: the Read tool over `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` lines 1 to 10, 43 to 55, 253 to 272, 403 to 407, 433 to 442 and 492 to 496; `CMD-STRIPPED-COUNT` with `TOKENS-STRUCT`; `CMD-PHRASE-COUNT` with `PHRASES-DOC`; the Grep tool count of pattern `^` over the file. + - Acceptance, all required: the Grep count is 496; the lines read match fact 2 exactly at 3 (`using System.Globalization;`), 4 (`using System.Threading;`), 45, 46, 47 (`/// `), 51, 52 (blank), 53, 255 (` }`), 256 (blank), 257 (`/// `), 270, 405, 435 (` }`), 436 (blank), 437 (`/// `), 440, 494 (` }`), 495 (` }`) and 496 (`}`) (any mismatch is `SPLIT ANCHOR MOVED`: stop); every `STRIPPED` value equals its base value in `TOKENS-STRUCT` and every `PHRASE` value equals its base value in `PHRASES-DOC` (any other value is `BASELINE TOKEN MISMATCH`: stop). The recorded values are the false-before half of the P1-T22 gate. +- [ ] [P0-T5] Re-derive the test-side anchors for TaskMaster.Test/TaskMaster.Test.csproj, TaskMaster/TaskMaster.csproj and the fixture partials under TaskMaster.Test/Ribbon, and record FEATURE/evidence/baseline/anchor-test-side.md. + - Command: `CMD-LINECOUNT`; the Grep tool with `-n` over `TaskMaster.Test/TaskMaster.Test.csproj` for `EngineToggleStateCoordinatorTests` and over `TaskMaster/TaskMaster.csproj` for `EngineToggleStateCoordinator`; the Grep tool count over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests*.cs` for `\[TestMethod\]`, `\[DataTestMethod\]`, `\[DataRow\(`, `\[TestClass\]` and `OnNotify`; the Grep tool over `TaskMaster.Test` for each `NEW-NAMES-964` name. + - Acceptance, all required: `PRODUCTION-FILES: 1` and `TEST-PARTIALS: 6`, with `LINES` 496 for the production file and 470, 77, 175, 277, 290 and 215 for the primary, PrimeFaultOrdering, PrimeRegistration, Race, RepeatFaultSuppression and ThrowingSink partials; the test csproj lists six fixture entries (352, 359 to 363) and the production csproj one coordinator entry (466); `[TestMethod]` totals 36, `[DataTestMethod]` 1, `[DataRow(` 3 and `[TestClass]` 1, recorded as `EXPECTED-CASES: 39`; `OnNotify` count 0; every `NEW-NAMES-964` name has 0 hits; no file named `EngineToggleStateCoordinatorTests.SinkGuard.cs`, `EngineToggleStateCoordinator.Prime.cs` or `EngineToggleStateCoordinator.Messages.cs` exists. Any mismatch is `TEST ANCHOR MOVED`: stop. +- [ ] [P0-T6] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record FEATURE/evidence/baseline/bootstrap-sdk.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version'` + - Acceptance: `SDK_MARKER=True`, `dotnet --version` prints a version string rather than the global.json error message, `EXIT_CODE: 0`. Installer lines carrying an absolute path are transcribed with REDACTED-PATH. +- [ ] [P0-T7] Restore the manifest tools from dotnet-tools.json with `dotnet tool restore` and record FEATURE/evidence/baseline/bootstrap-tool-restore.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool restore; "RESTORE_EXIT=$LASTEXITCODE"; dotnet tool list --local'` + - Acceptance: `RESTORE_EXIT=0` and the local tool list contains a row whose Package Id is `csharpier` and whose Version is `1.2.6`. Only the Package Id and Version columns are transcribed (the Manifest column carries an absolute path). +- [ ] [P0-T8] Restore NuGet packages with scripts/vscode/Invoke-Restore.ps1 and record FEATURE/evidence/baseline/bootstrap-nuget-restore.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; $env:MSBUILDDISABLENODEREUSE = "1"; & (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1"); "RESTORE_EXIT=$LASTEXITCODE"; "PACKAGE_DIRS=$(@(Get-ChildItem -LiteralPath packages -Directory -ErrorAction SilentlyContinue).Count)"; foreach ($proj in @("TaskMaster\TaskMaster.csproj", "TaskMaster.Test\TaskMaster.Test.csproj")) { $dir = Split-Path -Parent $proj; [xml]$x = Get-Content -LiteralPath $proj -Raw; $missing = @($x.SelectNodes("//*[local-name()=""Analyzer""]") | Where-Object { -not (Test-Path -LiteralPath (Join-Path $dir $_.GetAttribute("Include"))) }).Count; "ANALYZER_MISSING $proj = $missing" }'` + - Acceptance: `RESTORE_EXIT=0`, `PACKAGE_DIRS=` at least 1, and both `ANALYZER_MISSING` values 0 (non-zero is `ANALYZER PATH SKEW`: stop). +- [ ] [P0-T9] Provision the dotnet-coverage global tool (guarded) for scripts/vscode/Invoke-MSTestWithCoverage.ps1 and record FEATURE/evidence/baseline/bootstrap-dotnet-coverage.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; if (-not (Get-Command dotnet-coverage -ErrorAction SilentlyContinue)) { dotnet tool install --global dotnet-coverage }; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"; dotnet-coverage --version'` + - Acceptance: `DOTNET_COVERAGE_RESOLVED=True`, a version line is printed, `EXIT_CODE: 0`. +- [ ] [P0-T10] Capture the read-only formatter baseline over the worktree (`.csharpierignore` applies) with `dotnet tool run csharpier check .` and record FEATURE/evidence/baseline/csharpier-check-baseline.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'` + - Acceptance: `EXIT_CODE:` is the printed `CSHARPIER_EXIT_CODE:` value, the `Checked N files` line is recorded, and every path CSharpier reports as unformatted is listed. `EXIT_CODE: 0` is required; a non-zero value is `FORMAT BASELINE NOT CLEAN`: stop, because the Phase 2 repository-wide format would then rewrite files outside the Write Set. +- [ ] [P0-T11] Capture the analyzer baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p0-t11) and record FEATURE/evidence/baseline/msbuild-analyzer-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded as `ANALYZER-BASELINE-WARNINGS:`; `TEST_DLL_EXISTS: True`. Non-zero exit is `ANALYZER BASELINE NOT CLEAN`: stop. +- [ ] [P0-T12] Capture the nullable baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p0-t12) and record FEATURE/evidence/baseline/msbuild-nullable-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded as `NULLABLE-BASELINE-WARNINGS:`; `TEST_DLL_EXISTS: True`. Non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop. +- [ ] [P0-T13] Capture the pre-change coordinator fixture run over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll with `CMD-VSTEST` (`TASKID` p0-t13, `NAMES` `NAMES-ALL`) and record FEATURE/evidence/baseline/coordinator-tests-baseline.md. + - Acceptance, all required: `EXIT_CODE: 0`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `COUNTERS` with `total` equal to `EXPECTED-CASES` (39) and `failed=0`, recorded as `BASELINE-TOTAL: 39`; every `INVARIANT-NAMES` entry has a `RESULT ... = Passed` line; no `RESULT` line names a `NEW-NAMES-964` entry; no `FAILED` line. Anything else is `EXISTING FIXTURE NOT GREEN AT BASE`: stop. +- [ ] [P0-T14] Capture the baseline repository-wide test-and-coverage run with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` (`STAGE` baseline) and then `CMD-COVERAGE-POST` (`STAGE` baseline, `RAW` per its rule), and record FEATURE/evidence/baseline/coverage-baseline.md. + - Artifact: `Timestamp:`; `Command:` `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`; `EXIT_CODE:` the `RUNNER_EXIT_CODE:`; `ExpectedExitCode:` equal to it when non-zero; `Output Summary:` (at most 20 lines) carrying the exit code, `RAW:`, `LINE-FLOOR:`, `BRANCH-FLOOR:`, the `First-party coverage:` line (repository line and branch headline), the `ROOT` line, `COORD-LINES`, `COORD-LINE-RATE:` (the coordinator class line coverage baseline, recorded also as `BASELINE-COORD-LINE-RATE:`) and `BASELINE-FAILED-FQN-COUNT:` (the `FAILED-FQN-COUNT:` value) in `Output Summary:` and every `FAILED-FQN` row, with `TEST-DEFINITIONS:`, under `Details:`; then `Details:` with `DISCOVERED_LINE:`, `THRESHOLD_MESSAGE:`, `COLLECT_FAILURE_MESSAGE:`, `DOCUMENT_PRESENT:`, `TRX_PRESENT:`, `SUMMARY_FILE_PRESENT:`, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the summary verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, every `COORD-FILE` row, `COORD-CLASS-NODES:`, `COORD-BRANCHES`, and every `METHOD` and `METHOD-LINE` row. + - Branches, checked in order: (d) `DOCUMENT_PRESENT: False` or `TRX_PRESENT: False` is `COVERAGE RUN ABORTED`: stop without `CMD-COVERAGE-POST`. (c) a non-empty `THRESHOLD_MESSAGE:`, `LINE-FLOOR: NOT MET` or `BRANCH-FLOOR: NOT MET` is `COVERAGE FLOOR BASELINE NOT MET`: record and stop. (b) a non-zero exit with a non-empty `COLLECT_FAILURE_MESSAGE:`, `TEST-DEFINITIONS:` at least 1, `FAILED-FQN-COUNT:` at least 1, no `FAILED-FQN` value beginning `UNRESOLVED:` and none beginning `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.` completes this task with those `FAILED-FQN` rows as the baseline failed set (the pre-existing local failures, for example the shell-icon test of fact 11; CI runs them). (a) exit 0 with both floors met completes it with `BASELINE-FAILED-FQN-COUNT: 0`. Any other outcome, including a non-zero exit with `FAILED-FQN-COUNT: 0` or with a `FAILED-FQN` value beginning `UNRESOLVED:`, is `COVERAGE RUN ABORTED`: stop. + - Acceptance, all required: branch (a) or (b); `COORD-CLASS-NODES: 1` with the main-file `COORD-FILE` row `nodes=1` and the Prime and Messages rows `nodes=0`; `COORD-LINES` valid at least 1; `METHOD HandleToggleClickAsync` and `METHOD CompletePrime` rows present with `elements=` at least 1, recorded as `BASELINE-METHOD-HTC-UNCOVERED:` and `BASELINE-METHOD-CP-UNCOVERED:`; `METHOD TryInvokeSink ABSENT` and `METHOD BuildNotifyFailedMessage ABSENT`; the projection contains a `package` named `TaskMaster` with `LINE` and `BRANCH` counters; the summary first line begins `Test run outcome:`; no absolute path in the artifact. `coverage\baseline-964.cobertura.xml` and `coverage\baseline-964.trx` stay on disk, git-ignored. + +### Phase 1 — Constrained Implementation: Behaviour-Preserving Split, Regression Tests First, Then the Fix + +Phase 1 is the constrained small-path implementation, executed task by task by the delegated executor. Ordering: the split (P1-T2 to P1-T8) lands and is proven behaviour-preserving before any test is written; the regression tests (P1-T9 to P1-T14) are recorded failing against the split but unfixed coordinator; the fix (P1-T15 to P1-T24) then turns them green. + +- [ ] [P1-T1] Record the implementation handoff for the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md in FEATURE/evidence/other/implementation-handoff.md. + - Acceptance: the artifact carries `Timestamp:`, names the delegated executor role (atomic-executor, small-path implementation), lists the eight Write Set code paths verbatim, states the implementation-completion criteria (P1-T24 pass-after gate, P1-T22 census gate, P1-T25 unchanged-partials gate and P1-T26 size gate all met), and records the Edit-route rule of D-11. +- [ ] [P1-T2] Create `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Write tool as delivered source S1, transcribing base lines 47 to 51 and 437 to 494 of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` verbatim (read with the Read tool). + - Acceptance: the file exists; its first two lines are `using System;` and `using System.Globalization;`; the Grep tool counts `internal sealed partial class EngineToggleStateCoordinator` 1, `private const string NullEngineNameToken` 1 and `private static string BuildUnmappedKeyMessage` 1 in it. The transcription itself is proved by P1-T7. +- [ ] [P1-T3] Create `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Write tool as delivered source S2, transcribing base lines 257 to 435 of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` verbatim. + - Acceptance: the file exists; its usings are exactly `System`, `System.Threading`, `System.Threading.Tasks` and `UtilitiesCS`; the Grep tool counts `internal Task GetPrimeTask` 1 and `private void CompletePrime` 1 in it. +- [ ] [P1-T4] Reduce `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit or Write tool to the 249-line main partial of delivered source S3. + - Acceptance: the Grep tool counts 0 in the file for each of `using System\.Globalization;`, `using System\.Threading;`, `NullEngineNameToken = `, `internal Task GetPrimeTask` and `private static string RenderEngineName`, and 1 for each of `internal sealed partial class EngineToggleStateCoordinator` and `internal async Task ExecuteToggleAsync`; the Grep count of pattern `^` over the file is 249. +- [ ] [P1-T5] Register the two new files in `TaskMaster/TaskMaster.csproj` with the Edit tool as delivered source C1. + - Acceptance: the Grep tool counts exactly 1 line for each of `Ribbon\x5CEngineToggleStateCoordinator\.cs"`, `Ribbon\x5CEngineToggleStateCoordinator\.Messages\.cs"` and `Ribbon\x5CEngineToggleStateCoordinator\.Prime\.cs"` in the file, the two new lines directly follow the existing entry (Read tool), and the numstat row of `git -C WORKTREE diff --numstat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster/TaskMaster.csproj` reports 2 inserted and 0 deleted lines. The Grep counts, the Read observation and the numstat row are recorded under `CSPROJ-REGISTRATION:` in FEATURE/evidence/regression-testing/split-census.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). +- [ ] [P1-T6] Format the three coordinator files under TaskMaster/Ribbon with `dotnet tool run csharpier format TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs`, then verify with `dotnet tool run csharpier check TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (both wrapped per the Toolchain commands convention). + - Acceptance: the format exits 0 (its `Formatted N files` line is a processed count, not an assertion); the success-case observation is the check run, which exits 0 and prints `Checked 3 files` with no path listed. Both outputs are recorded in FEATURE/evidence/regression-testing/split-census.md under `FORMAT:` (appended to the file P1-T5 created). +- [ ] [P1-T7] Prove the split is a pure move with `CMD-SPLIT-CENSUS` over the three files under TaskMaster/Ribbon and append the output to FEATURE/evidence/regression-testing/split-census.md. + - Acceptance: the output consists of exactly these eight difference lines and the `BASE-LINES:` line: `MISSING x1 :: internal sealed class EngineToggleStateCoordinator`, `EXTRA x3 :: internal sealed partial class EngineToggleStateCoordinator`, `EXTRA x2 :: namespace TaskMaster`, `EXTRA x4 :: {`, `EXTRA x4 :: }`, `EXTRA x2 :: using System;`, `EXTRA x1 :: using System.Threading.Tasks;`, `EXTRA x1 :: using UtilitiesCS;`. Any other `MISSING` or `EXTRA` line is `SPLIT NOT A PURE MOVE`: correct the transcription with the Edit tool against the base lines and re-run P1-T6 and this task; after two failed corrections stop. `BASE-LINES: 496` is required. +- [ ] [P1-T8] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t8) and run the unchanged fixture against the split coordinator with `CMD-VSTEST` (`TASKID` p1-t8, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/split-fixture-green.md. + - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `CSC_OUT_TASKMASTER:` at least 1 (the split files compiled); `VSTEST_EXIT_CODE: 0`, `SEQUENCE_FILES: 0`, `COUNTERS` total equal to `BASELINE-TOTAL` (39) with `failed=0`; every `INVARIANT-NAMES` entry `Passed`. Anything else is `SPLIT CHANGED BEHAVIOUR`: stop. +- [ ] [P1-T9] Add the `OnNotify` harness member to `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` with the Edit tool as delivered source T1. + - Acceptance: the Grep tool counts `internal Action OnNotify` 1, `OnNotify\?\.Invoke\(message\);` 1 and `Notifications\.Add\(message\),` 0 in the file, and `Notifications\.Add\(message\);` 1. +- [ ] [P1-T10] Create `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` with the Write tool as delivered source T2. + - Acceptance: the file exists and the Grep tool counts `\[TestMethod\]` 4 and `\[TestClass\]` 0 in it, and each `NEW-NAMES-964` name exactly once. +- [ ] [P1-T11] Register the new partial in `TaskMaster.Test/TaskMaster.Test.csproj` with the Edit tool as delivered source C2. + - Acceptance: the Grep tool counts exactly 1 line for `Ribbon\x5CEngineToggleStateCoordinatorTests\.SinkGuard\.cs"` in the file, directly after the RepeatFaultSuppression entry (Read tool), and the numstat row of `git -C WORKTREE diff --numstat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster.Test/TaskMaster.Test.csproj` reports 1 inserted and 0 deleted lines. The Grep count, the Read observation and the numstat row are recorded under `TEST-CSPROJ-REGISTRATION:` in FEATURE/evidence/regression-testing/sink-guard-partial-tokens.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). +- [ ] [P1-T12] Reword the stale remark in `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` with the Edit tool as delivered source T3 (related defect D-7a). + - Acceptance: the Grep tool counts `logs a second error` 0 and `Since issue #948 that second cancellation is a repeat of a kind already` 1 in the file; P1-T25 proves no code line changed. +- [ ] [P1-T13] Format the three touched test files under TaskMaster.Test/Ribbon with `dotnet tool run csharpier format TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs`, verify with `dotnet tool run csharpier check` over the same three paths (both wrapped per the Toolchain commands convention), and append to FEATURE/evidence/regression-testing/sink-guard-partial-tokens.md. + - Acceptance, all required: the check run exits 0 and prints `Checked 3 files` with no path listed (the success-case observation of the write-mode format); in the SinkGuard partial the Grep tool counts 0 for each of `Thread\.Sleep`, `Task\.Delay`, `DoNotParallelize`, `GetTempPath`, `File\.`, `DateTime\.Now` and `DateTime\.UtcNow`, and at least 2 for `TaskCompletionSource` (positive control that the file was read); the reason fragments `a throwing notification sink must not escape the refusal path`, `the refusal path contains a failure of both sinks` and `notify sink failed` each occur whole on one physical line (Grep count at least 1 each). +- [ ] [P1-T14] [expect-fail] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t14) and run the fixture with the new tests against the split but unfixed coordinator with `CMD-VSTEST` (`TASKID` p1-t14, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/refusal-path-fail-before.md with `ExpectedExitCode:` equal to the observed non-zero exit. + - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_TASKMASTER_TEST:` at least 1; `CMD-STRIPPED-COUNT` with `TOKENS-STRUCT`, run immediately before the test run, prints the base value for every token except the two split tokens (`"internalsealedclassEngineToggleStateCoordinator"` 0 and `"internalsealedpartialclassEngineToggleStateCoordinator"` 3), proving the fix is absent from the coordinator under test; `SEQUENCE_FILES: 0`; `COUNTERS` total equal to `BASELINE-TOTAL` plus 4 (43) with `failed=3`; the `FAILED` lines name exactly the three `FAIL-BEFORE-NAMES`; `RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain = Passed` and every `INVARIANT-NAMES` entry `Passed`. Reason gate: the first name's `MESSAGE` contains both `a throwing notification sink must not escape the refusal path` and `notify sink failed`; the second's contains `threw exception` and `notify sink failed`; the third's contains `the refusal path contains a failure of both sinks` and `notify sink failed`. A `FAIL-BEFORE-NAMES` entry that passes is `FAIL-BEFORE NOT REPRODUCED`: stop; one whose message lacks its two fragments is `FAIL-BEFORE WRONG REASON`: stop; the `GUARD-NAME` failing is `INVARIANT GUARD RED AT BASE`: stop; any other failed test is `UNEXPECTED FAILURE`: stop. Absolute paths in messages are transcribed as REDACTED-PATH. +- [ ] [P1-T15] Apply delivered source F1 (constructor parameter docs) and F2 (`GetPressed` returns) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. + - Acceptance: the Grep tool counts `and must not throw: its` 1, `an exception it throws is reported once through` 1, `Receives an observed prime fault, toggle fault or notification failure as a message` 1 and `honours its non-throwing precondition` 1 in the file. +- [ ] [P1-T16] Apply delivered source F3 (`HandleToggleClickAsync` summary, remarks and body) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. + - Acceptance: the Grep tool counts `_notifyUnavailable\(BuildUnavailableMessage\(engineName\)\);` 0 and `The other two are sink guards` 0 in the file, and `out var notifyFailure` 1. +- [ ] [P1-T17] Insert delivered source F4 (`TryInvokeSink`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool, directly after the closing brace of `ExecuteToggleAsync`. + - Acceptance: the Grep tool counts `private static bool TryInvokeSink\(Action sinkCall, out Exception sinkFailure\)` 1 in the file. +- [ ] [P1-T18] Apply delivered source F5 (`GetPrimeTask` documentation) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. + - Acceptance: the Grep tool counts `The in-flight` 0 and `The prime task, or` 0 in the file, and `The marker is not the` at least 1. +- [ ] [P1-T19] Apply delivered source F6 (`StartObservedPrime` remarks) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. + - Acceptance: the Grep tool counts `The three` 0 and `all sit in` 0 in the file. +- [ ] [P1-T20] Apply delivered source F7a, F7b and F7c (`CompletePrime` summary, remarks and guarded report) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. The record `_reportedPrimeFaults[reportKey] = 0;` must be the only statement of the branch taken when `TryInvokeSink` returns `true`, directly after the guarded sink call; it is never placed before the call, in an `else` branch or after the branch (D-3, the #948 invariant that a throwing sink leaves the report owed). + - Acceptance: the Grep tool counts `catch \(Exception\)` 0, `sink failure is contained here` 0 and `_reportedPrimeFaults\[reportKey\] = 0;` 1 in the file. +- [ ] [P1-T21] Insert delivered source F8 (`BuildNotifyFailedMessage`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Edit tool, directly after the closing brace of `BuildToggleFailedMessage`, then format the three coordinator files under TaskMaster/Ribbon with the P1-T6 format command and verify them with the P1-T6 check command (both wrapped per the Toolchain commands convention). + - Acceptance: the Grep tool counts `private static string BuildNotifyFailedMessage\(string engineName\)` 1 in the Messages file; the check run exits 0 and prints `Checked 3 files` with no path listed (the success-case observation of the write-mode format). Both outputs and the Grep count are recorded under `FORMAT:` in FEATURE/evidence/qa-gates/production-edit-scope.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). +- [ ] [P1-T22] Verify the edit scope of the three files under TaskMaster/Ribbon (AC3, AC5, AC7) with `CMD-STRIPPED-COUNT` (`TOKENS-STRUCT`), `CMD-PHRASE-COUNT` (`PHRASES-DOC`) and `CMD-PROTECTED-SPANS` (`SIGNATURES-PROTECTED`), and append to FEATURE/evidence/qa-gates/production-edit-scope.md. + - Acceptance, all required: every `STRIPPED` value equals its required final value in `TOKENS-STRUCT`; every `PHRASE` value equals its required final value in `PHRASES-DOC`; every `SPAN-HASH` line reads `equal=True` (twelve signatures and two field declarations). Together with the P0-T4 base values this shows each token and phrase moving from its false-before to its true-after value. The artifact also records, from reading the formatted files with the Read tool, the line ranges of the two remaining `catch` clauses (one in `HandleToggleClickAsync`, one in `TryInvokeSink`) as `CATCH-SITES:`. Any mismatch is `EDIT SCOPE MISMATCH`: correct the edit against the delivered source with the Edit tool and re-run P1-T21 and this task; after two failed corrections stop. +- [ ] [P1-T23] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t23) and record the build lines at the top of FEATURE/evidence/regression-testing/refusal-path-pass-after.md (this task creates the file). + - Acceptance: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `CSC_OUT_TASKMASTER:` at least 1 and `TEST_DLL_ADVANCED: True`. +- [ ] [P1-T24] Run the fixture against the fixed coordinator with `CMD-VSTEST` over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll (`TASKID` p1-t24, `NAMES` `NAMES-ALL`) and append the result to FEATURE/evidence/regression-testing/refusal-path-pass-after.md. + - Acceptance, all required: `VSTEST_EXIT_CODE: 0`; `SEQUENCE_FILES: 0`; `COUNTERS` total equal to `BASELINE-TOTAL` plus 4 (43), `passed` equal to `total`, `failed=0`; every `NEW-NAMES-964` and every `INVARIANT-NAMES` entry has a `RESULT ... = Passed` line; no `FAILED` line. Anything else is `PASS-AFTER NOT MET`: stop and report. +- [ ] [P1-T25] Verify that no existing test assertion in TaskMaster.Test/Ribbon was weakened or removed (AC4) with `CMD-TEST-DIFF`, and record FEATURE/evidence/qa-gates/test-partials-unchanged.md. + - Acceptance, all required: the four `UNCHANGED` lines read `exit=0` (PrimeFaultOrdering, PrimeRegistration, ThrowingSink and RepeatFaultSuppression byte-equal to BASE-SHA); for `EngineToggleStateCoordinatorTests.Race.cs` `NON-DOC-CHANGES` is 0; for `EngineToggleStateCoordinatorTests.cs` the `DIFF` line shows `minus=1` and the single `MINUS` line reads `message => Notifications.Add(message),`. Any other result is `EXISTING TEST CHANGED`: stop. +- [ ] [P1-T26] Measure every coordinator source file under TaskMaster/Ribbon and every fixture partial under TaskMaster.Test/Ribbon (AC6) with `CMD-LINECOUNT` and `CMD-HASH`, check the csproj registrations, and record FEATURE/evidence/qa-gates/file-line-counts.md. + - Command: `CMD-LINECOUNT`; `CMD-HASH`; the Grep tool counts of `Ribbon\x5CEngineToggleStateCoordinator\.` over `TaskMaster/TaskMaster.csproj` and of `Ribbon\x5CEngineToggleStateCoordinatorTests` over `TaskMaster.Test/TaskMaster.Test.csproj`. + - Acceptance, all required: `PRODUCTION-FILES: 3` and every production `LINES` value at most 450 (expected about 307, 196 and 86 for the main, Prime and Messages files; observations only); `TEST-PARTIALS: 7` and every test `LINES` value at most 500 (primary fixture expected 481, Race 277, SinkGuard about 175); the production csproj has 3 coordinator compile entries and the test csproj 7 fixture entries, each file named by a `LINES` row registered exactly once. The `HASH` values are recorded as `PHASE1-HASHES:` for P2-T1. Any production file over 450 or test file over 500 is `FILE SIZE CEILING EXCEEDED`: stop. + +### Phase 2 — Final QA Toolchain Loop, Coverage Delta, Scope and Acceptance + +No code file is edited in Phase 2. If P2-T1 rewrites a Write Set file, the loop restarts once from P2-T1 (recorded as `PASS-2:` sections in the same artifacts); any other failure of P2-T1 to P2-T5 stops the plan with its artifact, and the fix returns to the orchestrator as a remediation round. + +- [ ] [P2-T1] Apply repository-wide formatting from the worktree root (TaskMaster.sln tree, `.csharpierignore` applies) with `dotnet tool run csharpier format .` and record FEATURE/evidence/qa-gates/csharpier-format.md. + - Command: `CMD-HASH` and `git -C WORKTREE status --porcelain --untracked-files=all` before the format; `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier format .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH` and the same porcelain command again. + - Acceptance, all required: `EXIT_CODE: 0`; the `Formatted N files` line is recorded as a processed count, not an assertion; the before-and-after tree observation holds: every `HASH` value after the format equals the value before it (in pass 1 the before values must also equal `PHASE1-HASHES:`) and the two porcelain listings are identical. In pass 1 a differing Write Set hash restarts the loop once from this task as stated above, and pass 2 compares against the hashes recorded after the pass-1 format; a differing hash in pass 2 is `FORMAT NOT STABLE`: stop. A changed porcelain entry outside the Write Set is `FORMAT TOUCHED OUT-OF-SCOPE FILE`: stop. +- [ ] [P2-T2] Verify formatting read-only from the worktree root (TaskMaster.sln tree) with `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/csharpier-check-final.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'` + - Acceptance: `EXIT_CODE: 0`, the `Checked N files` line is recorded, and no path is reported as unformatted. +- [ ] [P2-T3] Run the analyzer gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p2-t3) and record FEATURE/evidence/qa-gates/msbuild-analyzer-final.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded beside `ANALYZER-BASELINE-WARNINGS:` as an observation. +- [ ] [P2-T4] Run the type-check gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p2-t4) and record FEATURE/evidence/qa-gates/msbuild-nullable-final.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded beside `NULLABLE-BASELINE-WARNINGS:` as an observation. +- [ ] [P2-T5] Run the test-and-coverage gate with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` (`STAGE` final) and then `CMD-COVERAGE-POST` (`STAGE` final, `RAW` per its rule), and record FEATURE/evidence/qa-gates/coverage-final.md with the same artifact layout as P0-T14 (numeric post-change figures in `Output Summary:`: the `First-party coverage:` line, the `ROOT` line, `COORD-LINES` and `COORD-LINE-RATE:`; `FINAL-FAILED-FQN-COUNT:` and every `FAILED-FQN` row). + - Acceptance, all required: `DOCUMENT_PRESENT: True` and `TRX_PRESENT: True`; `LINE-FLOOR: MET`, `BRANCH-FLOOR: MET` and an empty `THRESHOLD_MESSAGE:`; the test-step rule of D-8 holds: `EXIT_CODE: 0`, or a non-zero exit with a non-empty `COLLECT_FAILURE_MESSAGE:`, `TEST-DEFINITIONS:` at least 1, `FAILED-FQN-COUNT:` at least 1, no `FAILED-FQN` value beginning `UNRESOLVED:` or `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.`, and every `FAILED-FQN` value present verbatim among the `FAILED-FQN` rows of `FEATURE/evidence/baseline/coverage-baseline.md` (then `ExpectedExitCode:` equals the observed value and the artifact records `TEST-STEP: PASS (PRE-EXISTING FAILURES ONLY)`); every `COORD-FILE` row reads `nodes=` at least 1 (otherwise `PARTIAL CLASS ATTRIBUTION UNSUPPORTED`: stop); the summary first line begins `Test run outcome:`; the projection contains the `TaskMaster` package; no absolute path in the artifact. A `FAILED-FQN` value absent from the baseline rows is `NEW FAILING TEST`: stop without a re-run. `coverage\final-964.cobertura.xml` and `coverage\final-964.trx` stay on disk, git-ignored. +- [ ] [P2-T6] Compare baseline and post-change coverage for the coordinator files under TaskMaster/Ribbon and record FEATURE/evidence/qa-gates/coverage-comparison.md (sources: FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-final.md). + - Acceptance, all required: the artifact carries `Timestamp:`, `Command:` (the two source artifacts read), `EXIT_CODE: 0` and an `Output Summary:` with `BASELINE-FIRST-PARTY:` and `FINAL-FIRST-PARTY:` (repository line and branch percentages), `BASELINE-COORD-LINES:`, `FINAL-COORD-LINES:`, `BASELINE-COORD-LINE-RATE:`, `FINAL-COORD-LINE-RATE:`, the four final `METHOD` rows and `NEW-CODE-COVERAGE:` (the `TryInvokeSink` and `BuildNotifyFailedMessage` rates). Clauses: `FINAL-COORD-LINE-RATE` at least `BASELINE-COORD-LINE-RATE` (AC8; otherwise `COORDINATOR COVERAGE LOWERED`: stop); `METHOD TryInvokeSink` and `METHOD BuildNotifyFailedMessage` rate at least 90.00; `METHOD HandleToggleClickAsync` uncovered at most `BASELINE-METHOD-HTC-UNCOVERED:` and `METHOD CompletePrime` uncovered at most `BASELINE-METHOD-CP-UNCOVERED:` (changed lines not reduced); both final floors met. Each clause is recorded `MET` or `NOT MET` with its two values; any `NOT MET` stops. +- [ ] [P2-T7] Record the single clean toolchain pass of TaskMaster.sln (P2-T1 to P2-T5) in FEATURE/evidence/qa-gates/toolchain-final-pass.md. + - Acceptance: the artifact carries `Timestamp:`, `Command:` listing the four CLAUDE.md commands verbatim in order (`dotnet tool run csharpier format .` with `dotnet tool run csharpier check .`; the analyzer `/t:Rebuild`; the `TreatWarningsAsErrors` `/t:Rebuild`; `Invoke-MSTestWithCoverage.ps1`), `EXIT_CODE: 0` and an `Output Summary:` naming each step's artifact and result, the pass number (1, or 2 after the admitted restart), and the D-8 test-step outcome. +- [ ] [P2-T8] Verify the change footprint of the worktree against BASE-SHA and the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md, and record FEATURE/evidence/qa-gates/footprint-scope.md. + - Command: `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance, all required: every path in the diff listing and every porcelain entry is one of the eight Write Set code paths, lies under the feature folder, equals the promotion record path, lies under `.claude/agent-memory/` (ambient, never staged), or appears in `INHERITED-PORCELAIN:` of P0-T2; positive control: the union of the two listings contains all eight Write Set code paths (the three created files appear as `??` entries in the porcelain listing, the five modified files in both). Any other path is `FOOTPRINT EXCEEDS WRITE SET`: stop. +- [ ] [P2-T9] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 and record FEATURE/evidence/qa-gates/evidence-hygiene.md. + - Acceptance: `FILES_SCANNED=` at least 33 (derivation: issue.md and this plan, 2; the 13 baseline, 5 regression-testing and 11 qa-gates evidence files of the Write Set other than evidence-hygiene.md, which this task writes after the sweep, 29; implementation-handoff.md, 1; the preparation-phase record, whose presence P0-T2 established, 1; 2 + 29 + 1 + 1 = 33), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of `CLEARANCE-PATH:` is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report; a non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder (the original stays under `coverage/`). +- [ ] [P2-T10] Check off AC1 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` per acceptance-criteria-tracking and append `AC1: MET` or `AC1: NOT MET` with its evidence to FEATURE/evidence/other/ac-status-summary.md (this task creates the file). + - Acceptance: AC1 is checked (`- [ ] AC1 (` becomes `- [x] AC1 (`, no other text changed) only when P1-T14 shows `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` Failed with its two fragments, P1-T24 shows it Passed, and no P2-T5 `FAILED-FQN` row equals `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow`; otherwise it stays unchecked with the failing values recorded. +- [ ] [P2-T11] Check off AC2 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: AC2 is checked only when P1-T24 shows `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing` and `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` Passed and P1-T14 shows both Failed for their recorded reasons; otherwise unchecked. +- [ ] [P2-T12] Check off AC3 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: AC3 is checked only when P1-T22 shows `"catch("` 2, `"catch(Exception)"` 0, `"TryInvokeSink("` 5 and each of the four call-site tokens 1, `CATCH-SITES:` names `HandleToggleClickAsync` and `TryInvokeSink` only, and P1-T25 shows the ThrowingSink partial `exit=0` with its four tests Passed at P1-T24; otherwise unchecked. +- [ ] [P2-T13] Check off AC4 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: AC4 is checked only when P1-T8 and P1-T24 show every `INVARIANT-NAMES` entry Passed with `failed=0`, P1-T24 shows `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` Passed, P1-T25 met every clause, and P1-T22 shows every `SPAN-HASH` `equal=True` and `"failure),out_)){_reportedPrimeFaults[reportKey]=0;}"` 1; otherwise unchecked. +- [ ] [P2-T14] Check off AC5 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: AC5 is checked only when P1-T22 shows `"The in-flight"`, `"most recently completed"` and `"The prime task, or"` 0 and `"The registration marker for an engine key"`, `"The marker is not the prime task itself"` and `"The registered marker, or"` 1, and a Read of the `GetPrimeTask` summary and returns in `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` confirms the text of delivered source F5; otherwise unchecked. +- [ ] [P2-T15] Check off AC6 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: AC6 is checked only when P1-T26 met every clause, P1-T7 met its census, and P2-T3 and P2-T4 show `CSC_OUT_TASKMASTER:` at least 1 with `ERRORS: 0`; otherwise unchecked. +- [ ] [P2-T16] Check off AC7 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: AC7 is checked only when every remaining `PHRASES-DOC` entry of P1-T22 holds its final value and a Read of the `HandleToggleClickAsync` summary and remarks, the `StartObservedPrime` remarks, the `CompletePrime` summary and remarks and the three constructor parameter docs confirms the delivered texts F1, F3, F6 and F7 against the code the comments describe; otherwise unchecked. +- [ ] [P2-T17] Check off AC8 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md, followed by the AC status summary block of acceptance-criteria-tracking (source, total 8, checked, remaining, items remaining). + - Acceptance: AC8 is checked only when P2-T7 records one clean pass of all four steps, P2-T5 met the D-8 test-step rule and both floors, and P2-T6 shows `FINAL-COORD-LINE-RATE` at least `BASELINE-COORD-LINE-RATE`; the AC8 line in the summary states the runner exit code and, when it is non-zero, the pre-existing `FAILED-FQN` values. Otherwise unchecked. +- [ ] [P2-T18] Hand off for the reduced (minor) audit and record FEATURE/evidence/other/reduced-audit-handoff.md. + - Acceptance: the artifact carries `Timestamp:`, the AC source (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`), the AC status counts copied from ac-status-summary.md, the evidence paths of every Phase 0, Phase 1 and Phase 2 artifact, the D-7 list of folded-in related defects, the statement that no commit was created (D-10), and the reduced artifact checks for the auditor: the fail-before artifact, the pass-after artifact, the coverage comparison, the footprint gate and the hygiene gate (P2-T9 and its P2-T20 final sweep). +- [ ] [P2-T19] Confirm the `## Write Set` section of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md enumerates every file the execution created or modified, and append the confirmation to FEATURE/evidence/other/reduced-audit-handoff.md under `WRITE-SET-CONFIRMATION:`. + - Acceptance: every code path of P2-T8's union listing is named in the `## Write Set` code list and every one of the eight code entries appears in that union; every evidence file named in the `## Write Set` evidence list exists on disk (Glob tool over FEATURE/evidence); the Glob tool over FEATURE/evidence/other for `preflight-clearance.*.md` returns exactly one path which, converted to repository-relative forward-slash form, equals `CLEARANCE-PATH:` of P0-T2, and `git -C WORKTREE hash-object CLEARANCE-PATH` (a working-tree hash, so the check holds whether or not anything has been committed since P0-T2) prints the `CLEARANCE-BLOB:` value of P0-T2, recorded as `CLEARANCE-BLOB-FINAL:`; and no other file exists under FEATURE/evidence, so any file named neither in the evidence list nor as the preparation-phase record is a discrepancy. A discrepancy is recorded and leaves this task unchecked. +- [ ] [P2-T20] Re-run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 after the AC check-offs and the audit handoff, and append the output under `FINAL-SWEEP:` to FEATURE/evidence/qa-gates/evidence-hygiene.md. + - Acceptance: `FILES_SCANNED=` at least 36 (derivation: issue.md and this plan, 2, plus the 33 evidence files of the Write Set — 13 baseline, 5 regression-testing, 12 qa-gates and 3 other — and the preparation-phase record, all of which exist once P2-T19 has passed; 2 + 33 + 1 = 36), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of `CLEARANCE-PATH:` is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report, because P2-T19 requires that record unchanged; a non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder. This task creates no new evidence file, so the P2-T19 evidence-set confirmation still holds. diff --git a/docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md b/docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md new file mode 100644 index 000000000..5c87945af --- /dev/null +++ b/docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md @@ -0,0 +1,69 @@ +# engine-toggle-coordinator-947-review-residuals (Issue #964) + +- Date captured: 2026-10-01 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/engine-toggle-coordinator-947-review-residuals/ (Issue #964) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #964 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/964 +- Last Updated: 2026-10-01 +## Summary + +The #947 review (PR #963) left three residuals in `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`: +1. On the refusal path of `HandleToggleClickAsync`, the "engines unavailable" notification call is still unguarded. A throwing notification sink can escape into the Office ribbon callback, so the method's "never throws" comment overstates that path. This is the same root cause as #947. +2. The `GetPrimeTask` doc comment opens with "The prime task", but the method returns the registration marker. +3. The file is 476 of 500 lines, so it needs splitting before its next change. + +## Environment + +- OS/version: Windows 11 (Outlook VSTO add-in) +- Python version: n/a (C#, .NET Framework 4.8) +- Command/flags used: review of PR #963 +- Data source or fixture: n/a + +## Steps to Reproduce + +1. Construct the coordinator with engines unavailable and a notification delegate that throws. +2. Invoke the toggle click. + +## Expected Behavior + +- The click handler never throws into the ribbon callback on any path. +- Doc comments match behavior. +- The file has room under the 500-line limit. + +## Actual Behavior + +- The notification exception escapes on the refusal path. +- The `GetPrimeTask` doc is inaccurate. +- The file is 476 lines. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: PR #963 body, Follow-ups 1 to 3; #947 review artifacts. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [x] Medium +- [ ] Low + +## Suspected Cause / Notes + +#947 guarded the two `_logError` call sites only. The duplicated guard could become one helper that is applied to both log sinks and notification sinks. + +## Proposed Fix / Validation Ideas + +- [ ] Write a regression test first: a throwing notification on the refusal path must not escape. Then add a shared "invoke sink safely" helper and use it at all three sites. +- [ ] Correct the `GetPrimeTask` doc comment. +- [ ] Split the file into partials before or with the change, keeping each partial under 500 lines. +- [ ] Sequence this with #948, which edits the same file. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch From 98934d356e6ab39167e3b56edc9a2aa78e60795b Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 22:37:56 -0400 Subject: [PATCH 02/17] docs(964): re-anchor the footprint gate after the main merge (plan v1.5) P2-T8 diffs against the merge commit 981abef77 with two negative controls; wording corrections from the confirming preflight. Confirming preflight ALL CLEAR; plan validator ok. No acceptance criterion changed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X979KwR3sAjLjLkJJtnTQR --- .../plan.2026-10-02T05-20.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 7f46d91cb..725e60948 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -4,9 +4,9 @@ - **Parent (optional):** none - **Owner:** drmoisan - **Work Mode:** minor-audit (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` line 12 reads `- Work Mode: minor-audit`) -- **Last Updated:** 2026-10-02T07-40 -- **Status:** Draft, revision round 3 applied (preflight deltas: forward-slash `CLEARANCE-PATH:`, per-file `HIT-FILE` attribution in CMD-HYGIENE, preparation-phase record counted in the P2-T9 and P2-T20 lower bounds), awaiting executor preflight -- **Version:** 1.3 (revision round 3: P0-T2 records `CLEARANCE-PATH:` in repository-relative forward-slash form and P2-T19 compares the Glob result after the same conversion; CMD-HYGIENE prints one `HIT-FILE` row per file with a non-zero host count, and P2-T9 and P2-T20 repair or stop by those rows; the P2-T9 lower bound becomes 33 and the P2-T20 lower bound becomes 36, each now counting the preparation-phase record, superseding the version 1.2 wording that scanned it outside the bounds). Version 1.2 (revision round 2: the `## Write Set` names the preparation-phase record `other/preflight-clearance..md`; P0-T2 records `CLEARANCE-PATH:` and `CLEARANCE-BLOB:`; P2-T19 admits exactly that one record and requires its working-tree hash to equal `CLEARANCE-BLOB:`; P2-T9 and P2-T20 state that the record is scanned outside their lower bounds and is never repaired by this plan). Version 1.1 (revision round 1: the pre-existing-failure comparison uses fully qualified test names (D1); P1-T5, P1-T11 and P1-T21 name and create their evidence artifacts (D2); a final host-path sweep P2-T20 runs after the last artifact is written (D3)). Version 1.0 was the initial authoring; the scaffold that occupied this path was replaced in full. +- **Last Updated:** 2026-10-02T21-45 +- **Status:** Approved (preflight ALL CLEAR round 4 on version 1.3); version 1.4 re-anchor and version 1.5 wording corrections applied under the coordinator standing authority, awaiting confirming preflight +- **Version:** 1.5 (confirming-preflight wording corrections under the coordinator standing authority, no acceptance criterion changes: the P2-T8 positive-control parenthetical describes files recorded by an orchestrator phase-boundary commit; D-10 and the version 1.4 note state that such commits leave the verdict of every footprint gate unchanged; P0-T3 names the MERGE-SHA exception; fact 10 cites `.dotnet*/` at 356 after the merge; P2-T18 states that no task of this plan created a commit). Version 1.4 (post-merge re-anchor under the coordinator standing authority: origin/main 993fdd015 was merged into the branch as MERGE-SHA 981abef77657adcc90d7c116a6b4c6500b79ea29; the whole-tree footprint gate P2-T8 now diffs against MERGE-SHA instead of BASE-SHA and carries two negative controls, one proving the check still reports an out-of-scope path and one proving the merge changed nothing under TaskMaster/ or TaskMaster.Test/; every other BASE-SHA anchor is unchanged because those anchors are scoped to TaskMaster/ and TaskMaster.Test/; wording only: the P2-T8 title names MERGE-SHA, and D-10 states the per-gate ref and that orchestrator phase-boundary commits leave the verdict of every footprint gate unchanged; no acceptance criterion changes). Version 1.3 (revision round 3: P0-T2 records `CLEARANCE-PATH:` in repository-relative forward-slash form and P2-T19 compares the Glob result after the same conversion; CMD-HYGIENE prints one `HIT-FILE` row per file with a non-zero host count, and P2-T9 and P2-T20 repair or stop by those rows; the P2-T9 lower bound becomes 33 and the P2-T20 lower bound becomes 36, each now counting the preparation-phase record, superseding the version 1.2 wording that scanned it outside the bounds). Version 1.2 (revision round 2: the `## Write Set` names the preparation-phase record `other/preflight-clearance..md`; P0-T2 records `CLEARANCE-PATH:` and `CLEARANCE-BLOB:`; P2-T19 admits exactly that one record and requires its working-tree hash to equal `CLEARANCE-BLOB:`; P2-T9 and P2-T20 state that the record is scanned outside their lower bounds and is never repaired by this plan). Version 1.1 (revision round 1: the pre-existing-failure comparison uses fully qualified test names (D1); P1-T5, P1-T11 and P1-T21 name and create their evidence artifacts (D2); a final host-path sweep P2-T20 runs after the last artifact is written (D3)). Version 1.0 was the initial authoring; the scaffold that occupied this path was replaced in full. - **Plan path continuity:** this file is updated in place for every revision round. No timestamped sibling plan file is created for this cycle. - **Execution topology:** The executor for this plan must be dispatched without worktree isolation: the Bash-tool isolation guard refuses every `pwsh` invocation in an isolated agent, and every toolchain step in this plan is a pwsh payload. @@ -20,7 +20,7 @@ - Sole requirements source: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`, section `## Acceptance Criteria` (line 23), eight checkbox lines `- [ ] AC1` through `- [ ] AC8` (lines 27 to 34 when this plan was authored; check-off tasks locate each line by its `- [ ] ACn (` prefix, never by line number). Only that section is an acceptance-criteria source. No `spec.md`, `user-story.md` or `research.md` exists in the feature folder or is required; P0-T2 fails closed if one appears. - Predecessors (merged into the base): issues #942, #944, #947 and #948, whose invariants AC4 preserves. -- Base: origin/main at `94287369908cc920b21b0e3256314f988ad7d2f5` (BASE-SHA). The branch `bug/engine-toggle-coordinator-947-review-residuals-964` was cut from it. Every `git diff` and `git show` in this plan uses that literal as its ref operand. +- Base: origin/main at `94287369908cc920b21b0e3256314f988ad7d2f5` (BASE-SHA). The branch `bug/engine-toggle-coordinator-947-review-residuals-964` was cut from it. Every `git diff` and `git show` in this plan uses that literal as its ref operand. Exception (version 1.4): P2-T8 diffs against MERGE-SHA 981abef77657adcc90d7c116a6b4c6500b79ea29, the merge of origin/main 993fdd015 into the branch. ## AC identity table @@ -46,7 +46,7 @@ 7. `UtilitiesCS/Interfaces/IGlobals/IAppItemEngines.cs` declares `IAppItemEngines` in namespace `UtilitiesCS` (line 5). `TaskMaster/TaskMaster.csproj` sets `LangVersion` `preview` (31) and generates no documentation file, so `cref` targets are not compiled into diagnostics. `.editorconfig` raises only `MSTEST0032` to warning (29); analyzer rules are suggestions. 8. `scripts/vscode/Invoke-MSTestWithCoverage.ps1`: parameters `SearchRoot`, `Configuration`, `CoverageOutput` (default `coverage\coverage.cobertura.xml`, 9); the inner vstest arguments hard-code `/TestCaseFilter:TestCategory!=LiveOutlook` (91), the results directory and the trx name (92, 93; fixed values `coverage\test-results` and `mstest-coverage-run.trx` at 297 and 298); a non-zero collector exit throws `MSTest with coverage failed with exit code N` (262) before post-processing, so the raw document and the trx stay on disk and no summary or projection is written; assembly discovery excludes `(^|\\)\.claude\\` relative to the search root (353), so the runner discovers this worktree's own test assemblies; on success it prints the `First-party coverage:` line (410), writes the JaCoCo projection beside the output (415 to 419), writes `coverage\test-results\mstest-coverage-run.summary.txt` (440 to 447) and retains the raw document because it sits in the repository coverage directory (449 to 453, `Test-RawCoverageDocumentRetained`); the entry guard at 459 makes the file safe to dot-source. 9. `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` defines `Get-CoberturaClassLineSummary -ClassNode` (160) and `Merge-CoberturaClassesByFilename` (260, groups class elements by `filename`), applied by `ConvertTo-KoverageCoberturaXml` (407, 442); after post-processing each source file is one class node (the #948 final run read `COORD-CLASS-NODES: 1` for the then single-file coordinator). `Invoke-MSTestWithCoverage.FirstParty.ps1` `Get-CoberturaFirstPartyCoverageReport` (123) renders `First-party coverage: lines a/b (x%), branches c/d (y%)` (117 to 120). `Invoke-MSTest.TrxSummary.ps1` `Format-TrxRunSummary` (103) renders five lines beginning `Test run outcome:`. -10. `scripts/vscode/Install-RepoDotNetSdk.ps1` installs SDK 8.0.205 to `.dotnet-sdk` with marker `.dotnet-sdk\sdk\8.0.205` (3, 56); `scripts/vscode/Invoke-Restore.ps1` exists; `global.json` pins 8.0.205 with paths `.dotnet-sdk` and the host. Neither `.dotnet-sdk\sdk` nor `packages\` exists in this worktree, so bootstrap is required. `.gitignore` ignores `*.trx` (146), `*cobertura*.xml` (147), `coverage/*` (150, `.gitkeep` re-included at 151), `**/[Pp]ackages/*` (197) and `.dotnet*/` (357). `.csharpierignore` excludes `**/evidence/**` (4), raw coverage and trx names (5 to 8), project files (12 to 14), `packages.config` (16) and `app.config` (18). +10. `scripts/vscode/Install-RepoDotNetSdk.ps1` installs SDK 8.0.205 to `.dotnet-sdk` with marker `.dotnet-sdk\sdk\8.0.205` (3, 56); `scripts/vscode/Invoke-Restore.ps1` exists; `global.json` pins 8.0.205 with paths `.dotnet-sdk` and the host. Neither `.dotnet-sdk\sdk` nor `packages\` exists in this worktree, so bootstrap is required. `.gitignore` ignores `*.trx` (146), `*cobertura*.xml` (147), `coverage/*` (150, `.gitkeep` re-included at 151), `**/[Pp]ackages/*` (197) and `.dotnet*/` (356 after the merge of origin/main 993fdd015, which replaced lines 257 and 258, `*.rptproj.bak` and `*.csproj.bak`, with the single line `*.bak` at 257). `.csharpierignore` excludes `**/evidence/**` (4), raw coverage and trx names (5 to 8), project files (12 to 14), `packages.config` (16) and `app.config` (18). 11. Observed outputs from the executed #947 and #948 runs on this machine (their evidence folders, 2026-10-01): the stall probe over the four UtilitiesCS.Test shell-icon classes exited 1 with one failed test (`GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension`, Win32 icon-handle `ArgumentException`), not a hang; the coordinator fixture fail-before message of a FluentAssertions `NotThrowAsync` assertion read `Did not expect any exception because , but found System.InvalidOperationException: sink failed / at ...`; an exception escaping a test method read `Test method threw exception: / : `; `dotnet tool run csharpier check .` printed `Checked N files in ms.` and named only unformatted paths; `First-party coverage: lines 56204/65855 (85.35%), branches 13618/17078 (79.74%)` and `COORD-LINES covered=177 valid=177` at the #948 final. 12. The four UtilitiesCS.Test shell-icon classes are `UtilitiesCS.Test.HelperClasses.ShellUtilities_Tests`, `UtilitiesCS.Test.HelperClasses.ShellUtilitiesStatic_Tests`, `UtilitiesCS.Test.HelperClasses.SysImageListHelperTests` and `UtilitiesCS.Test.EmailIntelligence.OSBrowser_Tests`; CI runs them unfiltered. 13. `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md` is the promotion record for #964; this plan never edits it. @@ -63,7 +63,7 @@ - **D-7 Folded-in related defects (maintainer directive 2026-10-02).** (a) the `.Race.cs` remark at 196 to 201 claims the re-prime "logs a second error", which #948 made false; the remark is reworded, no code changes. (b) the `GetPressed` returns sentence and the `logError` parameter doc (D-5). (c) the missing throwing-sink-leaves-report-owed test (D-6). No completely unrelated defect was found. - **D-8 Coverage route and AC8 test-step rule.** Step 4 runs `scripts/vscode/Invoke-MSTestWithCoverage.ps1` verbatim (by absolute path from the worktree cwd, fact 8). The runner's exit code is recorded. AC8's own qualifier ("no new failing test relative to baseline") defines the test-step pass condition: the step passes when the runner exits 0, or when it exits non-zero with `MSTest with coverage failed with exit code` and the trx-derived set of fully qualified failed names (`FAILED-FQN` rows of `CMD-COVERAGE-POST`, each the `TestMethod` `className` up to its first comma, a dot, and the `TestMethod` `name`) is non-empty, `TEST-DEFINITIONS:` is at least 1, no value begins with `UNRESOLVED:`, every value appears verbatim as a `FAILED-FQN` row of `FEATURE/evidence/baseline/coverage-baseline.md`, and no value begins with `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.`. When the runner exits non-zero it writes no summary or projection (fact 8), so `CMD-COVERAGE-POST` post-processes the raw document with the runner's own helpers (`RAW True`); when it exits 0, the document is already post-processed (`RAW False`). Both floors (80% line, 75% branch, applied by the runner's threshold functions) must be met in every case. A run that hangs is bounded by wall clock and stops the plan; no alternative route is taken. - **D-9 Coordinator coverage figure (AC8).** The coordinator figure is the sum, over every class node whose normalised `filename` ends with one of the three coordinator source paths, of `Get-CoberturaClassLineSummary` covered and valid lines; the rate is 100 times covered over valid, rounded to two decimals. Baseline has one file; final has three. Final rate must be at least the baseline rate. Per-method rows for `HandleToggleClickAsync`, `CompletePrime`, `TryInvokeSink` and `BuildNotifyFailedMessage` are read from the node of the file that contains the method: the two new methods must reach at least 90.00 (CLAUDE.md new-code target) and the two changed methods must have no more uncovered elements than at baseline. At final, each of the three files must have at least one class node; otherwise `PARTIAL CLASS ATTRIBUTION UNSUPPORTED`: stop and report, because the per-file figures would then be unmeasurable. -- **D-10 No commits.** This plan creates no commit and stages nothing. Every footprint gate compares the working tree against BASE-SHA with `git diff --name-only BASE-SHA` paired with `git status --porcelain --untracked-files=all` in the same task. `.claude/agent-memory/` paths are ambient state of other sessions, are never staged, and are admitted by the footprint gate. Committing is the orchestrator's step after the reduced audit. +- **D-10 No commits.** This plan creates no commit and stages nothing. Every footprint gate compares the working tree against a ref (BASE-SHA, except P2-T8, which uses MERGE-SHA from version 1.4) with `git diff --name-only` paired with `git status --porcelain --untracked-files=all` in the same task. `.claude/agent-memory/` paths are ambient state of other sessions, are never staged, and are admitted by the footprint gate. Committing is the orchestrator's step; orchestrator commits at phase boundaries leave the verdict of every footprint gate unchanged: each `git diff` compares the working tree to a ref, so a file such a commit records leaves the porcelain listing but remains in the diff listing, and no verdict that reads the diff alone or the union of the two listings changes. - **D-11 Edit route.** Every `.cs` and `.csproj` change is made with the Edit or Write tool, never through a shell write, so the repository hooks see it. A hook denial of any Edit or Write is reported verbatim and stops that step; the executor does not retry with a rephrased edit or another tool. ## Write Set (every file this plan creates or modifies) @@ -830,7 +830,7 @@ At baseline only the main file exists, so `TryInvokeSink` and `BuildNotifyFailed - Acceptance, all required: the artifact records that issue.md line 12 reads `- Work Mode: minor-audit`; that a heading line exactly `## Acceptance Criteria` exists; that the section holds exactly 8 lines beginning `- [ ] AC` and 0 beginning `- [x] AC` (counted with the Grep tool, patterns `^- \[ \] AC[1-8] ` and `^- \[x\] AC`); that the Glob result for the three names is `none` (a hit is `UNEXPECTED REQUIREMENTS DOCUMENT`: stop, per the minor-audit fail-closed rule); the eight code paths of the Write Set verbatim; and `INHERITED-PORCELAIN:` with every entry, each of which must lie under the feature folder, equal `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md`, or lie under `.claude/agent-memory/` (any other entry is `UNEXPECTED INHERITED CHANGE`: stop); and the preparation-phase record of the Write Set: the Glob result for `preflight-clearance.*.md` is exactly one path, recorded as `CLEARANCE-PATH:`, and `CLEARANCE-BLOB:` is the 40-hex-digit blob that `rev-parse` prints for it (zero or several paths, or a `rev-parse` failure because the record is not committed at HEAD, is `PREPARATION RECORD MISSING`: stop). - [ ] [P0-T3] Verify the base anchor of the branch for `TaskMaster/` and `TaskMaster.Test/` against BASE-SHA and record FEATURE/evidence/baseline/anchor-production.md (this task creates the file; P0-T4 appends to it). - Command: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse origin/main`; `git -C WORKTREE merge-base 94287369908cc920b21b0e3256314f988ad7d2f5 HEAD`; `git -C WORKTREE diff --exit-code --stat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test`. - - Acceptance, all required: the merge-base output equals `94287369908cc920b21b0e3256314f988ad7d2f5` (otherwise `BASE NOT ANCESTOR`: stop); the diff exits 0 and prints nothing (`ANCHOR-CODE-DIFF-EXIT=0`; otherwise `CODE DIFFERS FROM BASE`: stop); `HEAD:` and `ORIGIN-MAIN:` are recorded as observations, and when `ORIGIN-MAIN:` differs from BASE-SHA the artifact records `ORIGIN-MAIN MOVED` without stopping, because every anchor in this plan is the BASE-SHA literal. + - Acceptance, all required: the merge-base output equals `94287369908cc920b21b0e3256314f988ad7d2f5` (otherwise `BASE NOT ANCESTOR`: stop); the diff exits 0 and prints nothing (`ANCHOR-CODE-DIFF-EXIT=0`; otherwise `CODE DIFFERS FROM BASE`: stop); `HEAD:` and `ORIGIN-MAIN:` are recorded as observations, and when `ORIGIN-MAIN:` differs from BASE-SHA the artifact records `ORIGIN-MAIN MOVED` without stopping, because every anchor in this plan is the BASE-SHA literal, except the MERGE-SHA anchor of P2-T8 (version 1.4). - [ ] [P0-T4] Verify the split anchors and the false-before token and phrase values of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, and append them to FEATURE/evidence/baseline/anchor-production.md. - Command: the Read tool over `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` lines 1 to 10, 43 to 55, 253 to 272, 403 to 407, 433 to 442 and 492 to 496; `CMD-STRIPPED-COUNT` with `TOKENS-STRUCT`; `CMD-PHRASE-COUNT` with `PHRASES-DOC`; the Grep tool count of pattern `^` over the file. - Acceptance, all required: the Grep count is 496; the lines read match fact 2 exactly at 3 (`using System.Globalization;`), 4 (`using System.Threading;`), 45, 46, 47 (`/// `), 51, 52 (blank), 53, 255 (` }`), 256 (blank), 257 (`/// `), 270, 405, 435 (` }`), 436 (blank), 437 (`/// `), 440, 494 (` }`), 495 (` }`) and 496 (`}`) (any mismatch is `SPLIT ANCHOR MOVED`: stop); every `STRIPPED` value equals its base value in `TOKENS-STRUCT` and every `PHRASE` value equals its base value in `PHRASES-DOC` (any other value is `BASELINE TOKEN MISMATCH`: stop). The recorded values are the false-before half of the P1-T22 gate. @@ -941,9 +941,9 @@ No code file is edited in Phase 2. If P2-T1 rewrites a Write Set file, the loop - Acceptance, all required: the artifact carries `Timestamp:`, `Command:` (the two source artifacts read), `EXIT_CODE: 0` and an `Output Summary:` with `BASELINE-FIRST-PARTY:` and `FINAL-FIRST-PARTY:` (repository line and branch percentages), `BASELINE-COORD-LINES:`, `FINAL-COORD-LINES:`, `BASELINE-COORD-LINE-RATE:`, `FINAL-COORD-LINE-RATE:`, the four final `METHOD` rows and `NEW-CODE-COVERAGE:` (the `TryInvokeSink` and `BuildNotifyFailedMessage` rates). Clauses: `FINAL-COORD-LINE-RATE` at least `BASELINE-COORD-LINE-RATE` (AC8; otherwise `COORDINATOR COVERAGE LOWERED`: stop); `METHOD TryInvokeSink` and `METHOD BuildNotifyFailedMessage` rate at least 90.00; `METHOD HandleToggleClickAsync` uncovered at most `BASELINE-METHOD-HTC-UNCOVERED:` and `METHOD CompletePrime` uncovered at most `BASELINE-METHOD-CP-UNCOVERED:` (changed lines not reduced); both final floors met. Each clause is recorded `MET` or `NOT MET` with its two values; any `NOT MET` stops. - [ ] [P2-T7] Record the single clean toolchain pass of TaskMaster.sln (P2-T1 to P2-T5) in FEATURE/evidence/qa-gates/toolchain-final-pass.md. - Acceptance: the artifact carries `Timestamp:`, `Command:` listing the four CLAUDE.md commands verbatim in order (`dotnet tool run csharpier format .` with `dotnet tool run csharpier check .`; the analyzer `/t:Rebuild`; the `TreatWarningsAsErrors` `/t:Rebuild`; `Invoke-MSTestWithCoverage.ps1`), `EXIT_CODE: 0` and an `Output Summary:` naming each step's artifact and result, the pass number (1, or 2 after the admitted restart), and the D-8 test-step outcome. -- [ ] [P2-T8] Verify the change footprint of the worktree against BASE-SHA and the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md, and record FEATURE/evidence/qa-gates/footprint-scope.md. - - Command: `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5`; `git -C WORKTREE status --porcelain --untracked-files=all`. - - Acceptance, all required: every path in the diff listing and every porcelain entry is one of the eight Write Set code paths, lies under the feature folder, equals the promotion record path, lies under `.claude/agent-memory/` (ambient, never staged), or appears in `INHERITED-PORCELAIN:` of P0-T2; positive control: the union of the two listings contains all eight Write Set code paths (the three created files appear as `??` entries in the porcelain listing, the five modified files in both). Any other path is `FOOTPRINT EXCEEDS WRITE SET`: stop. +- [ ] [P2-T8] Verify the change footprint of the worktree against MERGE-SHA and the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md, and record FEATURE/evidence/qa-gates/footprint-scope.md. + - Command: `git -C WORKTREE diff --name-only 981abef77657adcc90d7c116a6b4c6500b79ea29` (MERGE-SHA: the merge of origin/main 993fdd015 into the branch, version 1.4); `git -C WORKTREE status --porcelain --untracked-files=all`; negative controls `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29` and `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29 -- TaskMaster TaskMaster.Test`. + - Acceptance, all required: every path in the diff listing and every porcelain entry is one of the eight Write Set code paths, lies under the feature folder, equals the promotion record path, lies under `.claude/agent-memory/` (ambient, never staged), or appears in `INHERITED-PORCELAIN:` of P0-T2; positive control: the union of the two listings contains all eight Write Set code paths (a Write Set code file recorded by an orchestrator phase-boundary commit after MERGE-SHA appears in the diff listing and need not appear in the porcelain listing; an uncommitted created file appears as a `??` entry in the porcelain listing, and an uncommitted modified file appears in both). Any other path is `FOOTPRINT EXCEEDS WRITE SET`: stop. Negative controls, both required: the first control listing is non-empty and contains the line `.gitignore`, a path outside the Write Set, which shows that the path-set check above reports `FOOTPRINT EXCEEDS WRITE SET` when an out-of-scope path differs from the anchor (otherwise `FOOTPRINT CONTROL INERT`: stop); the second control listing is empty, which shows the move of the anchor from BASE-SHA to MERGE-SHA hides no change under TaskMaster/ or TaskMaster.Test/ (otherwise `MERGE TOUCHED ITEM CODE`: stop). Both control listings are recorded in the artifact. - [ ] [P2-T9] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 and record FEATURE/evidence/qa-gates/evidence-hygiene.md. - Acceptance: `FILES_SCANNED=` at least 33 (derivation: issue.md and this plan, 2; the 13 baseline, 5 regression-testing and 11 qa-gates evidence files of the Write Set other than evidence-hygiene.md, which this task writes after the sweep, 29; implementation-handoff.md, 1; the preparation-phase record, whose presence P0-T2 established, 1; 2 + 29 + 1 + 1 = 33), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of `CLEARANCE-PATH:` is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report; a non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder (the original stays under `coverage/`). - [ ] [P2-T10] Check off AC1 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` per acceptance-criteria-tracking and append `AC1: MET` or `AC1: NOT MET` with its evidence to FEATURE/evidence/other/ac-status-summary.md (this task creates the file). @@ -963,7 +963,7 @@ No code file is edited in Phase 2. If P2-T1 rewrites a Write Set file, the loop - [ ] [P2-T17] Check off AC8 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md, followed by the AC status summary block of acceptance-criteria-tracking (source, total 8, checked, remaining, items remaining). - Acceptance: AC8 is checked only when P2-T7 records one clean pass of all four steps, P2-T5 met the D-8 test-step rule and both floors, and P2-T6 shows `FINAL-COORD-LINE-RATE` at least `BASELINE-COORD-LINE-RATE`; the AC8 line in the summary states the runner exit code and, when it is non-zero, the pre-existing `FAILED-FQN` values. Otherwise unchecked. - [ ] [P2-T18] Hand off for the reduced (minor) audit and record FEATURE/evidence/other/reduced-audit-handoff.md. - - Acceptance: the artifact carries `Timestamp:`, the AC source (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`), the AC status counts copied from ac-status-summary.md, the evidence paths of every Phase 0, Phase 1 and Phase 2 artifact, the D-7 list of folded-in related defects, the statement that no commit was created (D-10), and the reduced artifact checks for the auditor: the fail-before artifact, the pass-after artifact, the coverage comparison, the footprint gate and the hygiene gate (P2-T9 and its P2-T20 final sweep). + - Acceptance: the artifact carries `Timestamp:`, the AC source (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`), the AC status counts copied from ac-status-summary.md, the evidence paths of every Phase 0, Phase 1 and Phase 2 artifact, the D-7 list of folded-in related defects, the statement that no task of this plan created a commit or staged a file (D-10), any commit after MERGE-SHA being an orchestrator phase-boundary commit, and the reduced artifact checks for the auditor: the fail-before artifact, the pass-after artifact, the coverage comparison, the footprint gate and the hygiene gate (P2-T9 and its P2-T20 final sweep). - [ ] [P2-T19] Confirm the `## Write Set` section of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md enumerates every file the execution created or modified, and append the confirmation to FEATURE/evidence/other/reduced-audit-handoff.md under `WRITE-SET-CONFIRMATION:`. - Acceptance: every code path of P2-T8's union listing is named in the `## Write Set` code list and every one of the eight code entries appears in that union; every evidence file named in the `## Write Set` evidence list exists on disk (Glob tool over FEATURE/evidence); the Glob tool over FEATURE/evidence/other for `preflight-clearance.*.md` returns exactly one path which, converted to repository-relative forward-slash form, equals `CLEARANCE-PATH:` of P0-T2, and `git -C WORKTREE hash-object CLEARANCE-PATH` (a working-tree hash, so the check holds whether or not anything has been committed since P0-T2) prints the `CLEARANCE-BLOB:` value of P0-T2, recorded as `CLEARANCE-BLOB-FINAL:`; and no other file exists under FEATURE/evidence, so any file named neither in the evidence list nor as the preparation-phase record is a discrepancy. A discrepancy is recorded and leaves this task unchecked. - [ ] [P2-T20] Re-run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 after the AC check-offs and the audit handoff, and append the output under `FINAL-SWEEP:` to FEATURE/evidence/qa-gates/evidence-hygiene.md. From 0561003e95a67b954fea2106abf7432cd2e61a13 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 22:48:20 -0400 Subject: [PATCH 03/17] docs(964): record Phase 0 tasks P0-T1 to P0-T3; P0-T4 halted by a hook block The P0-T4 phrase-count payload was denied by the promotion-only hook. Evidence and plan check-offs for P0-T1 to P0-T3 are committed so nothing is lost. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X979KwR3sAjLjLkJJtnTQR --- .../evidence/baseline/anchor-production.md | 78 +++++++++++++++++++ .../baseline/phase0-instructions-read.md | 21 +++++ .../evidence/baseline/scope-and-anchor.md | 49 ++++++++++++ .../plan.2026-10-02T05-20.md | 6 +- 4 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/phase0-instructions-read.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/scope-and-anchor.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md new file mode 100644 index 000000000..dad1135e7 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md @@ -0,0 +1,78 @@ +# Production Anchor (P0-T3, P0-T4) + +## P0-T3 — base anchor of the branch + +Timestamp: 2026-10-02T22-39 +Task: P0-T3 +Command: git rev-parse HEAD; git rev-parse origin/main; git merge-base 94287369908cc920b21b0e3256314f988ad7d2f5 HEAD; git diff --exit-code --stat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test +EXIT_CODE: 0 + +Output Summary: +- MERGE-BASE: 94287369908cc920b21b0e3256314f988ad7d2f5 (equals BASE-SHA; BASE is an ancestor of HEAD). +- ANCHOR-CODE-DIFF-EXIT=0 (the diff printed nothing: TaskMaster/ and TaskMaster.Test/ equal BASE-SHA). +- HEAD: 98934d356e6ab39167e3b56edc9a2aa78e60795b +- ORIGIN-MAIN: 993fdd01566dee82e5f37acb761a600feaaa1454 +- ORIGIN-MAIN MOVED (origin/main differs from BASE-SHA; recorded without stopping, because every anchor in the plan is the BASE-SHA literal except the MERGE-SHA anchor of P2-T8). +- Verdict: PASS (no BASE NOT ANCESTOR, no CODE DIFFERS FROM BASE). + +## P0-T4 — split anchors and false-before values (INCOMPLETE: blocked by a hook) + +Timestamp: 2026-10-02T22-41 +Task: P0-T4 +Command: Read tool over TaskMaster/Ribbon/EngineToggleStateCoordinator.cs lines 1-10, 43-55, 253-272, 403-407, 433-442, 492-496; CMD-STRIPPED-COUNT with TOKENS-STRUCT; CMD-PHRASE-COUNT with PHRASES-DOC (blocked); Grep count of `^` +EXIT_CODE: BLOCKED + +Output Summary: +- Grep line count: 496 (matches). +- Split anchor lines: all match fact 2 (no SPLIT ANCHOR MOVED). +- CMD-STRIPPED-COUNT: all 16 STRIPPED values equal their TOKENS-STRUCT base values. +- CMD-PHRASE-COUNT: NOT RUN. The PreToolUse Bash hook denied the payload before execution (verbatim message under Details). Per the binding instructions the command was not rephrased or restructured. +- Task status: unchecked; plan execution stopped at P0-T4. + +Details: + +Anchor lines observed (Read tool): +- 3 `using System.Globalization;` +- 4 `using System.Threading;` +- 45 ` internal sealed class EngineToggleStateCoordinator` +- 46 ` {` +- 47 ` /// ` +- 51 ` private const string NullEngineNameToken = "(null)";` +- 52 (blank) +- 53 ` private readonly Func _enginesAccessor;` +- 255 ` }` +- 256 (blank) +- 257 ` /// ` +- 270 ` internal Task GetPrimeTask(string engineName)` +- 405 ` private void CompletePrime(Task completed, string engineName)` +- 435 ` }` +- 436 (blank) +- 437 ` /// ` +- 440 ` private static string RenderEngineName(string engineName)` +- 494 ` }` +- 495 ` }` +- 496 `}` + +STRIPPED rows (observed; base value per TOKENS-STRUCT in parentheses): +- STRIPPED [catch(] = 3 (3) +- STRIPPED [catch(Exceptionex)] = 1 (1) +- STRIPPED [catch(Exception)] = 2 (2) +- STRIPPED [TryInvokeSink(] = 0 (0) +- STRIPPED [_logError(] = 2 (2) +- STRIPPED [_notifyUnavailable(] = 1 (1) +- STRIPPED [TryInvokeSink(()=>_notifyUnavailable(BuildUnavailableMessage(engineName)),outvarnotifyFailure)] = 0 (0) +- STRIPPED [TryInvokeSink(()=>_logError(BuildNotifyFailedMessage(engineName),notifyFailure),out_)] = 0 (0) +- STRIPPED [TryInvokeSink(()=>_logError(BuildToggleFailedMessage(engineName),ex),out_)] = 0 (0) +- STRIPPED [failure),out_)){_reportedPrimeFaults[reportKey]=0;}] = 0 (0) +- STRIPPED [_reportedPrimeFaults[reportKey]=0;] = 1 (1) +- STRIPPED [privatestaticboolTryInvokeSink(ActionsinkCall,outExceptionsinkFailure)] = 0 (0) +- STRIPPED [privatestaticstringBuildNotifyFailedMessage(stringengineName)] = 0 (0) +- STRIPPED [internalsealedclassEngineToggleStateCoordinator] = 1 (1) +- STRIPPED [internalsealedpartialclassEngineToggleStateCoordinator] = 0 (0) +- STRIPPED [_primeTasks.TryRemove(engineName,out_);] = 1 (1) + +HOOK BLOCK (verbatim, CMD-PHRASE-COUNT payload with PHRASES-DOC substituted): +``` +PreToolUse:Bash hook error: PROMOTION_MCP_ONLY_BLOCKED: Direct GitHub issue creation via `gh` bypasses the approved drm-copilot MCP promotion path (`mcp__drm-copilot__new_potential_entry` -> `mcp__drm-copilot__potential_to_issue` -> `mcp__drm-copilot__new_active_feature_folder`). Use those MCP tools instead. +``` +The message is the gh-issue-creation reason of `.claude/hooks/enforce-promotion-mcp-only.ps1` (emitted for `gh issue create`, `gh issue new`, or `gh api` against an issues endpoint with an explicit POST). The payload contains none of those command forms and invokes no `gh`; it does contain the word `issue` (phrase `The call is guarded (issue #964)`), the word `New-Object`, and the letter sequence `gh` inside `through`. The CMD-STRIPPED-COUNT payload, which carries no `issue` text, was allowed. Which scanner path matched is not determined; the block appears to be a false positive of the hook's command scanner on this payload text. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/phase0-instructions-read.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/phase0-instructions-read.md new file mode 100644 index 000000000..998b9a120 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/phase0-instructions-read.md @@ -0,0 +1,21 @@ +# Phase 0 Instructions Read (P0-T1) + +Timestamp: 2026-10-02T22-39 +Task: P0-T1 +Command: Read tool over the six policy documents below, in the order listed (no shell command) +EXIT_CODE: 0 +Policy Order: CLAUDE.md -> .claude/rules/general-code-change.md -> .claude/rules/general-unit-test.md -> .claude/rules/csharp.md + +Files read: +- CLAUDE.md +- .claude/rules/general-code-change.md +- .claude/rules/general-unit-test.md +- .claude/rules/csharp.md +- .claude/rules/tonality.md +- .claude/rules/plan-acceptance-gates.md + +Output Summary: +- All six files were read in full from the item worktree with the Read tool. +- The four core policies were read in the mandatory order; tonality.md and plan-acceptance-gates.md were read after them. +- No policy document was modified. +- Points relevant to this plan: C# toolchain order is csharpier format/check, msbuild /t:Rebuild analyzer gate, msbuild /t:Rebuild /p:TreatWarningsAsErrors=true nullable gate (no /p:Nullable=enable), then Invoke-MSTestWithCoverage.ps1; MSTest + Moq + FluentAssertions; 500-line file limit; no temporary files in tests; raw trx and raw Cobertura documents are never committed. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/scope-and-anchor.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/scope-and-anchor.md new file mode 100644 index 000000000..611e4f7dc --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/scope-and-anchor.md @@ -0,0 +1,49 @@ +# Scope and Requirements Anchor (P0-T2) + +Timestamp: 2026-10-02T22-39 +Task: P0-T2 +Command: git status --porcelain --untracked-files=all; Glob (spec.md, user-story.md, research*.md) over FEATURE; Glob preflight-clearance.*.md over FEATURE/evidence/other; git rev-parse HEAD:CLEARANCE-PATH +EXIT_CODE: 0 + +Output Summary: +- issue.md line 12 reads `- Work Mode: minor-audit`. +- Heading line exactly `## Acceptance Criteria` exists (issue.md line 23). +- Grep count `^- \[ \] AC[1-8] ` = 8; Grep count `^- \[x\] AC` = 0. +- Glob for spec.md, user-story.md, research*.md under FEATURE: none. +- INHERITED-PORCELAIN: 6 entries, every entry under FEATURE or under .claude/agent-memory/ (no UNEXPECTED INHERITED CHANGE). +- CLEARANCE-PATH: exactly one path found. +- CLEARANCE-BLOB: 40-hex blob resolved at HEAD (record is committed). +- Verdict: PASS (no stop condition). + +Details: + +Work Mode: `- Work Mode: minor-audit` (issue.md line 12) +AC heading: `## Acceptance Criteria` (issue.md line 23) +AC-UNCHECKED-COUNT: 8 +AC-CHECKED-COUNT: 0 +REQUIREMENTS-DOCUMENT-GLOB: none + +Write Set code paths (verbatim from the plan): +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (modify: split, then fix) +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (create) +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (create) +- `TaskMaster/TaskMaster.csproj` (modify: two compile items) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (modify: the `OnNotify` harness member) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (create) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (modify: remark only, D-7a) +- `TaskMaster.Test/TaskMaster.Test.csproj` (modify: one compile item) + +INHERITED-PORCELAIN: +``` + M docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +?? .claude/agent-memory/atomic-planner/project_964_partial_split_sink_guard_plan_seams.md +?? .claude/agent-memory/atomic-planner/project_964_r2_preparation_record_closed_evidence_set.md +?? .claude/agent-memory/atomic-planner/project_964_r3_glob_backslash_and_hit_attribution_seams.md +?? .claude/agent-memory/orchestrator/preparation-clearance-record-breaks-closed-evidence-set.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/phase0-instructions-read.md +``` +Note: the plan modification is the P0-T1 check-off and the untracked baseline file is the P0-T1 artifact; both lie under the feature folder. The four `.claude/agent-memory/` notes are ambient state of other sessions and are never staged. + +CLEARANCE-PATH: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md +(The Glob tool returned an absolute backslash-separated path; it was converted to this repository-relative forward-slash form before recording.) +CLEARANCE-BLOB: 2b1223012d7bd3551f7d9e2c9aa5539156a23cd2 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 725e60948..dace8475b 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -823,12 +823,12 @@ At baseline only the main file exists, so `TryInvokeSink` and `BuildNotifyFailed ### Phase 0 — Policy Reads, Anchor and Baseline Capture -- [ ] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/tonality.md and .claude/rules/plan-acceptance-gates.md, and record FEATURE/evidence/baseline/phase0-instructions-read.md. +- [x] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/tonality.md and .claude/rules/plan-acceptance-gates.md, and record FEATURE/evidence/baseline/phase0-instructions-read.md. - Acceptance: the artifact carries `Timestamp:`, a `Policy Order:` line naming CLAUDE.md, general-code-change.md, general-unit-test.md and csharp.md in that order, and a `Files read:` list naming all six repository-relative paths, one per line. No policy document is modified. -- [ ] [P0-T2] Read `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` in full and this plan, and record the requirements anchor and the Write Set in FEATURE/evidence/baseline/scope-and-anchor.md. +- [x] [P0-T2] Read `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` in full and this plan, and record the requirements anchor and the Write Set in FEATURE/evidence/baseline/scope-and-anchor.md. - Command: `git -C WORKTREE status --porcelain --untracked-files=all` (recorded verbatim as `INHERITED-PORCELAIN:`); the Glob tool over `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964` for `spec.md`, `user-story.md` and `research*.md`; the Glob tool over `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other` for `preflight-clearance.*.md` (recorded as `CLEARANCE-PATH:` in repository-relative form with forward-slash separators, `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance..md`; the Glob tool can return a backslash-separated or absolute path, which is converted to that form before it is recorded, because `git rev-parse HEAD:` does not resolve a backslash-separated tree path); `git -C WORKTREE rev-parse HEAD:CLEARANCE-PATH` with the recorded path substituted (recorded as `CLEARANCE-BLOB:`). - Acceptance, all required: the artifact records that issue.md line 12 reads `- Work Mode: minor-audit`; that a heading line exactly `## Acceptance Criteria` exists; that the section holds exactly 8 lines beginning `- [ ] AC` and 0 beginning `- [x] AC` (counted with the Grep tool, patterns `^- \[ \] AC[1-8] ` and `^- \[x\] AC`); that the Glob result for the three names is `none` (a hit is `UNEXPECTED REQUIREMENTS DOCUMENT`: stop, per the minor-audit fail-closed rule); the eight code paths of the Write Set verbatim; and `INHERITED-PORCELAIN:` with every entry, each of which must lie under the feature folder, equal `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md`, or lie under `.claude/agent-memory/` (any other entry is `UNEXPECTED INHERITED CHANGE`: stop); and the preparation-phase record of the Write Set: the Glob result for `preflight-clearance.*.md` is exactly one path, recorded as `CLEARANCE-PATH:`, and `CLEARANCE-BLOB:` is the 40-hex-digit blob that `rev-parse` prints for it (zero or several paths, or a `rev-parse` failure because the record is not committed at HEAD, is `PREPARATION RECORD MISSING`: stop). -- [ ] [P0-T3] Verify the base anchor of the branch for `TaskMaster/` and `TaskMaster.Test/` against BASE-SHA and record FEATURE/evidence/baseline/anchor-production.md (this task creates the file; P0-T4 appends to it). +- [x] [P0-T3] Verify the base anchor of the branch for `TaskMaster/` and `TaskMaster.Test/` against BASE-SHA and record FEATURE/evidence/baseline/anchor-production.md (this task creates the file; P0-T4 appends to it). - Command: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse origin/main`; `git -C WORKTREE merge-base 94287369908cc920b21b0e3256314f988ad7d2f5 HEAD`; `git -C WORKTREE diff --exit-code --stat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test`. - Acceptance, all required: the merge-base output equals `94287369908cc920b21b0e3256314f988ad7d2f5` (otherwise `BASE NOT ANCESTOR`: stop); the diff exits 0 and prints nothing (`ANCHOR-CODE-DIFF-EXIT=0`; otherwise `CODE DIFFERS FROM BASE`: stop); `HEAD:` and `ORIGIN-MAIN:` are recorded as observations, and when `ORIGIN-MAIN:` differs from BASE-SHA the artifact records `ORIGIN-MAIN MOVED` without stopping, because every anchor in this plan is the BASE-SHA literal, except the MERGE-SHA anchor of P2-T8 (version 1.4). - [ ] [P0-T4] Verify the split anchors and the false-before token and phrase values of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, and append them to FEATURE/evidence/baseline/anchor-production.md. From 08511aa6e285a91388a5eec36b5249860ce8da82 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 07:42:21 -0400 Subject: [PATCH 04/17] docs(964): record Phase 0 anchors, bootstrap and baseline evidence P0-T4 to P0-T14 completed; P0-T4 PHRASE rows recorded from the coordinator-run CMD-PHRASE-COUNT under the maintainer one-time bypass (2026-10-03). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../evidence/baseline/anchor-production.md | 85 ++++++++++- .../evidence/baseline/anchor-test-side.md | 50 +++++++ .../baseline/bootstrap-dotnet-coverage.md | 12 ++ .../baseline/bootstrap-nuget-restore.md | 16 ++ .../evidence/baseline/bootstrap-sdk.md | 21 +++ .../baseline/bootstrap-tool-restore.md | 18 +++ .../baseline/coordinator-tests-baseline.md | 32 ++++ .../evidence/baseline/coverage-baseline.md | 139 ++++++++++++++++++ .../baseline/csharpier-check-baseline.md | 12 ++ .../baseline/msbuild-analyzer-baseline.md | 16 ++ .../baseline/msbuild-nullable-baseline.md | 16 ++ .../plan.2026-10-02T05-20.md | 22 +-- 12 files changed, 427 insertions(+), 12 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-test-side.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-dotnet-coverage.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-nuget-restore.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-sdk.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-tool-restore.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coordinator-tests-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/csharpier-check-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-analyzer-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-nullable-baseline.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md index dad1135e7..8172f4b5d 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md @@ -15,7 +15,7 @@ Output Summary: - ORIGIN-MAIN MOVED (origin/main differs from BASE-SHA; recorded without stopping, because every anchor in the plan is the BASE-SHA literal except the MERGE-SHA anchor of P2-T8). - Verdict: PASS (no BASE NOT ANCESTOR, no CODE DIFFERS FROM BASE). -## P0-T4 — split anchors and false-before values (INCOMPLETE: blocked by a hook) +## P0-T4 — first attempt (INCOMPLETE: blocked by a hook; superseded by the completed run below) Timestamp: 2026-10-02T22-41 Task: P0-T4 @@ -76,3 +76,86 @@ HOOK BLOCK (verbatim, CMD-PHRASE-COUNT payload with PHRASES-DOC substituted): PreToolUse:Bash hook error: PROMOTION_MCP_ONLY_BLOCKED: Direct GitHub issue creation via `gh` bypasses the approved drm-copilot MCP promotion path (`mcp__drm-copilot__new_potential_entry` -> `mcp__drm-copilot__potential_to_issue` -> `mcp__drm-copilot__new_active_feature_folder`). Use those MCP tools instead. ``` The message is the gh-issue-creation reason of `.claude/hooks/enforce-promotion-mcp-only.ps1` (emitted for `gh issue create`, `gh issue new`, or `gh api` against an issues endpoint with an explicit POST). The payload contains none of those command forms and invokes no `gh`; it does contain the word `issue` (phrase `The call is guarded (issue #964)`), the word `New-Object`, and the letter sequence `gh` inside `through`. The CMD-STRIPPED-COUNT payload, which carries no `issue` text, was allowed. Which scanner path matched is not determined; the block appears to be a false positive of the hook's command scanner on this payload text. + +## P0-T4 — split anchors and false-before values (completed run) + +Timestamp: 2026-10-03T07-34 +Task: P0-T4 +Command: Read tool over TaskMaster/Ribbon/EngineToggleStateCoordinator.cs lines 1-10, 43-55, 253-272, 403-407, 433-442, 492-496; CMD-STRIPPED-COUNT with TOKENS-STRUCT; CMD-PHRASE-COUNT with PHRASES-DOC (coordinator-run, see below); Grep tool count of pattern `^` over the file +EXIT_CODE: 0 + +Output Summary: +- Pre-record checks: `git rev-parse HEAD` = 0561003e95a67b954fea2106abf7432cd2e61a13; `git diff --exit-code --stat 0561003e95a67b954fea2106abf7432cd2e61a13 -- TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` exited 0 and printed nothing (file unchanged against that commit). +- Grep line count: 496 (matches). +- Split anchor lines: all 20 cited lines match fact 2 (no SPLIT ANCHOR MOVED). +- CMD-STRIPPED-COUNT (executor-run): all 16 STRIPPED values equal their TOKENS-STRUCT base values. +- CMD-PHRASE-COUNT: all 24 PHRASE values equal their PHRASES-DOC base values (coordinator-run under the maintainer one-time bypass; rows below). +- Verdict: PASS (no SPLIT ANCHOR MOVED, no BASELINE TOKEN MISMATCH). These values are the false-before half of the P1-T22 gate. + +Details: + +Anchor lines observed (Read tool, this run): +- 3 `using System.Globalization;` +- 4 `using System.Threading;` +- 45 ` internal sealed class EngineToggleStateCoordinator` +- 46 ` {` +- 47 ` /// ` +- 51 ` private const string NullEngineNameToken = "(null)";` +- 52 (blank) +- 53 ` private readonly Func _enginesAccessor;` +- 255 ` }` +- 256 (blank) +- 257 ` /// ` +- 270 ` internal Task GetPrimeTask(string engineName)` +- 405 ` private void CompletePrime(Task completed, string engineName)` +- 435 ` }` +- 436 (blank) +- 437 ` /// ` +- 440 ` private static string RenderEngineName(string engineName)` +- 494 ` }` +- 495 ` }` +- 496 `}` + +STRIPPED rows (executor-run this pass; base value per TOKENS-STRUCT in parentheses): +- STRIPPED [catch(] = 3 (3) +- STRIPPED [catch(Exceptionex)] = 1 (1) +- STRIPPED [catch(Exception)] = 2 (2) +- STRIPPED [TryInvokeSink(] = 0 (0) +- STRIPPED [_logError(] = 2 (2) +- STRIPPED [_notifyUnavailable(] = 1 (1) +- STRIPPED [TryInvokeSink(()=>_notifyUnavailable(BuildUnavailableMessage(engineName)),outvarnotifyFailure)] = 0 (0) +- STRIPPED [TryInvokeSink(()=>_logError(BuildNotifyFailedMessage(engineName),notifyFailure),out_)] = 0 (0) +- STRIPPED [TryInvokeSink(()=>_logError(BuildToggleFailedMessage(engineName),ex),out_)] = 0 (0) +- STRIPPED [failure),out_)){_reportedPrimeFaults[reportKey]=0;}] = 0 (0) +- STRIPPED [_reportedPrimeFaults[reportKey]=0;] = 1 (1) +- STRIPPED [privatestaticboolTryInvokeSink(ActionsinkCall,outExceptionsinkFailure)] = 0 (0) +- STRIPPED [privatestaticstringBuildNotifyFailedMessage(stringengineName)] = 0 (0) +- STRIPPED [internalsealedclassEngineToggleStateCoordinator] = 1 (1) +- STRIPPED [internalsealedpartialclassEngineToggleStateCoordinator] = 0 (0) +- STRIPPED [_primeTasks.TryRemove(engineName,out_);] = 1 (1) + +PHRASE rows: coordinator-run under the maintainer one-time bypass of enforce-promotion-mcp-only.ps1 (2026-10-03), worktree agent-a3fb26aa2afc7c52c, HEAD 0561003e9, PowerShell tool (PRELUDE plus CMD-PHRASE-COUNT with PHRASES-DOC; base value per PHRASES-DOC in parentheses): +- PHRASE [The in-flight] = 1 (1) +- PHRASE [most recently completed] = 1 (1) +- PHRASE [The prime task, or] = 1 (1) +- PHRASE [The registration marker for an engine key] = 0 (0) +- PHRASE [The marker is not the prime task itself] = 0 (0) +- PHRASE [The registered marker, or] = 0 (0) +- PHRASE [The other two are sink guards] = 1 (1) +- PHRASE [which holds the only other catch clause] = 0 (0) +- PHRASE [the only catch clause in this type that observes an engine fault] = 1 (1) +- PHRASE [The three] = 1 (1) +- PHRASE [all sit in] = 1 (1) +- PHRASE [The two catch clauses in this type are the click boundary] = 0 (0) +- PHRASE [also contains a failure of the sink] = 1 (1) +- PHRASE [routes its sink call through] = 0 (0) +- PHRASE [a sink failure is contained here] = 1 (1) +- PHRASE [a sink failure is contained by] = 0 (0) +- PHRASE [by the statement directly after the sink call] = 1 (1) +- PHRASE [by the only statement of the branch taken when] = 0 (0) +- PHRASE [never throws, even when the sink throws] = 1 (1) +- PHRASE [never throws on either path, even when both sinks throw] = 0 (0) +- PHRASE [honours its non-throwing precondition] = 0 (0) +- PHRASE [and must not throw] = 0 (0) +- PHRASE [The call is guarded (issue #964)] = 0 (0) +- PHRASE [Receives an observed prime fault, toggle fault or notification failure] = 0 (0) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-test-side.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-test-side.md new file mode 100644 index 000000000..f31023d94 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-test-side.md @@ -0,0 +1,50 @@ +# Test-Side Anchor (P0-T5) + +Timestamp: 2026-10-03T07-35 +Task: P0-T5 +Command: CMD-LINECOUNT; Grep tool -n over TaskMaster.Test/TaskMaster.Test.csproj for `EngineToggleStateCoordinatorTests` and over TaskMaster/TaskMaster.csproj for `EngineToggleStateCoordinator`; Grep tool counts over TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests*.cs for `\[TestMethod\]`, `\[DataTestMethod\]`, `\[DataRow\(`, `\[TestClass\]`, `OnNotify`; Grep tool over TaskMaster.Test for each NEW-NAMES-964 name +EXIT_CODE: 0 + +Output Summary: +- PRODUCTION-FILES: 1; TEST-PARTIALS: 6. +- LINES: production 496; primary 470, PrimeFaultOrdering 77, PrimeRegistration 175, Race 277, RepeatFaultSuppression 290, ThrowingSink 215. +- Test csproj fixture entries: 6 (lines 352, 359, 360, 361, 362, 363); production csproj coordinator entry: 1 (line 466). +- [TestMethod] 36; [DataTestMethod] 1; [DataRow( 3; [TestClass] 1. +- EXPECTED-CASES: 39 +- OnNotify count: 0. NEW-NAMES-964 hits: 0 for every name. +- No EngineToggleStateCoordinatorTests.SinkGuard.cs, EngineToggleStateCoordinator.Prime.cs or EngineToggleStateCoordinator.Messages.cs exists (CMD-LINECOUNT enumeration lists none). +- Verdict: PASS (no TEST ANCHOR MOVED). + +Details: + +CMD-LINECOUNT output: +``` +LINES TaskMaster\Ribbon\EngineToggleStateCoordinator.cs = 496 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs = 470 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.PrimeFaultOrdering.cs = 77 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.PrimeRegistration.cs = 175 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.Race.cs = 277 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.RepeatFaultSuppression.cs = 290 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.ThrowingSink.cs = 215 +PRODUCTION-FILES: 1 +TEST-PARTIALS: 6 +``` + +TaskMaster.Test/TaskMaster.Test.csproj (Grep -n): +``` +352: +359: +360: +361: +362: +363: +``` + +TaskMaster/TaskMaster.csproj (Grep -n): +``` +466: +``` + +[TestMethod] per file: ThrowingSink 4, RepeatFaultSuppression 7, Race 6, PrimeFaultOrdering 1, PrimeRegistration 3, primary 15 (total 36). [DataTestMethod], [DataRow( (3) and [TestClass] all in the primary file. + +NEW-NAMES-964 search over TaskMaster.Test (one alternation pattern of the four names): 0 matches. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-dotnet-coverage.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-dotnet-coverage.md new file mode 100644 index 000000000..261126c34 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-dotnet-coverage.md @@ -0,0 +1,12 @@ +# Bootstrap: dotnet-coverage global tool (P0-T9) + +Timestamp: 2026-10-03T07-36 +Task: P0-T9 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; if (-not (Get-Command dotnet-coverage -ErrorAction SilentlyContinue)) { dotnet tool install --global dotnet-coverage }; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"; dotnet-coverage --version' +EXIT_CODE: 0 + +Output Summary: +- dotnet-coverage was already on PATH, so the guarded install did not run. +- DOTNET_COVERAGE_RESOLVED=True +- Version line: 18.10.0+f4cc39224845ffa74bf246c9da2399d50e5d6342 +- Verdict: PASS. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-nuget-restore.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-nuget-restore.md new file mode 100644 index 000000000..6252f209d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-nuget-restore.md @@ -0,0 +1,16 @@ +# Bootstrap: NuGet restore (P0-T8) + +Timestamp: 2026-10-03T07-36 +Task: P0-T8 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; $env:MSBUILDDISABLENODEREUSE = "1"; & (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1"); "RESTORE_EXIT=$LASTEXITCODE"; "PACKAGE_DIRS=..."; foreach ($proj in @("TaskMaster\TaskMaster.csproj", "TaskMaster.Test\TaskMaster.Test.csproj")) { ... "ANALYZER_MISSING $proj = $missing" }' (full payload as written in plan task P0-T8) +EXIT_CODE: 0 + +Output Summary: +- MSBuild Restore of TaskMaster.sln: Build succeeded, 0 Warning(s), 0 Error(s). +- RESTORE_EXIT=0 +- PACKAGE_DIRS=172 +- ANALYZER_MISSING TaskMaster\TaskMaster.csproj = 0 +- ANALYZER_MISSING TaskMaster.Test\TaskMaster.Test.csproj = 0 +- Verdict: PASS (no ANALYZER PATH SKEW). + +Details: the restore log (package-by-package lines carrying absolute paths) is not transcribed; only the summary lines above are recorded. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-sdk.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-sdk.md new file mode 100644 index 000000000..2cb35892c --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-sdk.md @@ -0,0 +1,21 @@ +# Bootstrap: repository .NET SDK (P0-T6) + +Timestamp: 2026-10-03T07-36 +Task: P0-T6 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version' +EXIT_CODE: 0 + +Output Summary: +- The marker was absent, so the installer ran and installed SDK 8.0.205. +- SDK_MARKER=True +- dotnet --version: 8.0.205 (a version string, not the global.json error message). +- Verdict: PASS. + +Details: +``` +Downloading .NET SDK 8.0.205 from https://builds.dotnet.microsoft.com/dotnet/Sdk/8.0.205/dotnet-sdk-8.0.205-win-x64.zip... +Installed repo-local .NET SDK 8.0.205 to REDACTED-PATH. +SDK_MARKER=True +8.0.205 +EXIT: 0 +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-tool-restore.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-tool-restore.md new file mode 100644 index 000000000..1cd391c7f --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-tool-restore.md @@ -0,0 +1,18 @@ +# Bootstrap: manifest tool restore (P0-T7) + +Timestamp: 2026-10-03T07-36 +Task: P0-T7 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool restore; "RESTORE_EXIT=$LASTEXITCODE"; dotnet tool list --local' +EXIT_CODE: 0 + +Output Summary: +- Tool 'csharpier' (version '1.2.6') was restored. Restore was successful. +- RESTORE_EXIT=0 +- Local tool list row: Package Id `csharpier`, Version `1.2.6`. +- Verdict: PASS. + +Details (Package Id and Version columns only; the Manifest column carries an absolute path): +``` +Package Id Version +csharpier 1.2.6 +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coordinator-tests-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coordinator-tests-baseline.md new file mode 100644 index 000000000..30f0c71c5 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coordinator-tests-baseline.md @@ -0,0 +1,32 @@ +# Baseline: coordinator fixture run (P0-T13) + +Timestamp: 2026-10-03T07-38 +Task: P0-T13 +Command: vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\p0-t13" "/Logger:trx;LogFileName=p0-t13.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +- VSTEST_EXIT_CODE: 0 +- TRX_PRESENT: True +- SEQUENCE_FILES: 0 +- COUNTERS total=39 executed=39 passed=39 failed=0 +- BASELINE-TOTAL: 39 +- All 11 INVARIANT-NAMES entries: Passed. No RESULT line names a NEW-NAMES-964 entry. No FAILED line. +- Verdict: PASS (no EXISTING FIXTURE NOT GREEN AT BASE). + +Details (CMD-VSTEST output lines, transcribed): +``` +COUNTERS total=39 executed=39 passed=39 failed=0 +RESULT GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged = Passed +RESULT GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns = Passed +RESULT GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly = Passed +RESULT GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly = Passed +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime = Passed +RESULT HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport = Passed +RESULT HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing = Passed +RESULT GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared = Passed +RESULT GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime = Passed +``` +The trx stays under the git-ignored coverage directory. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md new file mode 100644 index 000000000..89f94b511 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md @@ -0,0 +1,139 @@ +# Baseline: repository test-and-coverage run (P0-T14) + +Timestamp: 2026-10-03T07-41 +Task: P0-T14 +Command: pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1 +EXIT_CODE: 0 + +Output Summary: +- RUNNER_EXIT_CODE: 0 (branch (a): exit 0 with both floors met) +- RAW: False +- LINE-FLOOR: MET +- BRANCH-FLOOR: MET +- First-party coverage: lines 56609/65855 (85.96%), branches 13680/17078 (80.10%) +- ROOT line-rate=0.859601 branch-rate=0.801031 lines-covered=56609 lines-valid=65855 +- COORD-LINES covered=177 valid=177 +- COORD-LINE-RATE: 100 +- BASELINE-COORD-LINE-RATE: 100 +- BASELINE-FAILED-FQN-COUNT: 0 +- BASELINE-METHOD-HTC-UNCOVERED: 0 +- BASELINE-METHOD-CP-UNCOVERED: 0 +- Verdict: PASS (COORD-CLASS-NODES: 1; main-file row nodes=1, Prime and Messages rows nodes=0; TryInvokeSink and BuildNotifyFailedMessage ABSENT as expected at baseline). + +Details: + +- DISCOVERED_LINE: Discovered 9 test assemblies. +- THRESHOLD_MESSAGE: (empty) +- COLLECT_FAILURE_MESSAGE: (empty) +- DOCUMENT_PRESENT: True +- TRX_PRESENT: True +- SUMMARY_FILE_PRESENT: True +- FAILED-SET: (empty) +- TEST-DEFINITIONS: 7374 +- FAILED-FQN-COUNT: 0 (no FAILED-FQN rows) + +SUMMARY-BEGIN +``` +Test run outcome: Completed +Total 7384, executed 7384, passed 7384, failed 0. +Skipped 0, derived as total minus executed rather than reported by the test platform. +Figures reported verbatim by the test platform: error 0, timeout 0, aborted 0, notExecuted 0, inconclusive 0. +Failed tests: none +``` +SUMMARY-END + +PROJECTION-BEGIN +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` +PROJECTION-END + +Coordinator rows: +``` +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.cs nodes=1 covered=177 valid=177 +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs nodes=0 covered=0 valid=0 +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs nodes=0 covered=0 valid=0 +COORD-CLASS-NODES: 1 +COORD-BRANCHES covered=39 valid=40 +METHOD HandleToggleClickAsync file=TaskMaster/Ribbon/EngineToggleStateCoordinator.cs span=182-205 nodes=1 elements=18 covered=18 uncovered=0 rate=100 +METHOD-LINE HandleToggleClickAsync 183 hits=1 +METHOD-LINE HandleToggleClickAsync 184 hits=1 +METHOD-LINE HandleToggleClickAsync 185 hits=1 +METHOD-LINE HandleToggleClickAsync 186 hits=1 +METHOD-LINE HandleToggleClickAsync 187 hits=1 +METHOD-LINE HandleToggleClickAsync 191 hits=1 +METHOD-LINE HandleToggleClickAsync 192 hits=1 +METHOD-LINE HandleToggleClickAsync 193 hits=1 +METHOD-LINE HandleToggleClickAsync 194 hits=1 +METHOD-LINE HandleToggleClickAsync 195 hits=1 +METHOD-LINE HandleToggleClickAsync 197 hits=1 +METHOD-LINE HandleToggleClickAsync 198 hits=1 +METHOD-LINE HandleToggleClickAsync 199 hits=1 +METHOD-LINE HandleToggleClickAsync 200 hits=1 +METHOD-LINE HandleToggleClickAsync 201 hits=1 +METHOD-LINE HandleToggleClickAsync 203 hits=1 +METHOD-LINE HandleToggleClickAsync 204 hits=1 +METHOD-LINE HandleToggleClickAsync 205 hits=1 +METHOD CompletePrime file=TaskMaster/Ribbon/EngineToggleStateCoordinator.cs span=405-435 nodes=1 elements=20 covered=20 uncovered=0 rate=100 +METHOD-LINE CompletePrime 406 hits=1 +METHOD-LINE CompletePrime 407 hits=1 +METHOD-LINE CompletePrime 408 hits=1 +METHOD-LINE CompletePrime 409 hits=1 +METHOD-LINE CompletePrime 412 hits=1 +METHOD-LINE CompletePrime 413 hits=1 +METHOD-LINE CompletePrime 414 hits=1 +METHOD-LINE CompletePrime 420 hits=1 +METHOD-LINE CompletePrime 421 hits=1 +METHOD-LINE CompletePrime 422 hits=1 +METHOD-LINE CompletePrime 424 hits=1 +METHOD-LINE CompletePrime 425 hits=1 +METHOD-LINE CompletePrime 426 hits=1 +METHOD-LINE CompletePrime 427 hits=1 +METHOD-LINE CompletePrime 428 hits=1 +METHOD-LINE CompletePrime 429 hits=1 +METHOD-LINE CompletePrime 431 hits=1 +METHOD-LINE CompletePrime 432 hits=1 +METHOD-LINE CompletePrime 434 hits=1 +METHOD-LINE CompletePrime 435 hits=1 +METHOD TryInvokeSink ABSENT +METHOD BuildNotifyFailedMessage ABSENT +``` + +The raw documents `coverage\baseline-964.cobertura.xml` and `coverage\baseline-964.trx` stay on disk under the git-ignored coverage directory. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/csharpier-check-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/csharpier-check-baseline.md new file mode 100644 index 000000000..32c5028a7 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/csharpier-check-baseline.md @@ -0,0 +1,12 @@ +# Baseline: CSharpier check (P0-T10) + +Timestamp: 2026-10-03T07-37 +Task: P0-T10 +Command: dotnet tool run csharpier check . +EXIT_CODE: 0 + +Output Summary: +- Checked 1637 files in 5038ms. +- CSHARPIER_EXIT_CODE: 0 +- Unformatted paths reported: none. +- Verdict: PASS (no FORMAT BASELINE NOT CLEAN). diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-analyzer-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-analyzer-baseline.md new file mode 100644 index 000000000..0d31db682 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-analyzer-baseline.md @@ -0,0 +1,16 @@ +# Baseline: analyzer rebuild (P0-T11) + +Timestamp: 2026-10-03T07-37 +Task: P0-T11 +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true +EXIT_CODE: 0 + +Output Summary: +- MSBUILD_EXIT_CODE: 0 +- ERRORS: 0 +- ANALYZER-BASELINE-WARNINGS: 0 +- CSC_OUT_TASKMASTER: 2 +- CSC_OUT_TASKMASTER_TEST: 2 +- WRITESET_DIAGNOSTIC_LINES: 0 +- TEST_DLL_EXISTS: True +- Verdict: PASS (no ANALYZER BASELINE NOT CLEAN). MSBuild resolved through vswhere; solution passed by absolute path; file log at coverage\logs\p0-t11.msbuild.log (git-ignored, not copied). diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-nullable-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-nullable-baseline.md new file mode 100644 index 000000000..66ba959a8 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-nullable-baseline.md @@ -0,0 +1,16 @@ +# Baseline: nullable rebuild (P0-T12) + +Timestamp: 2026-10-03T07-38 +Task: P0-T12 +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true +EXIT_CODE: 0 + +Output Summary: +- MSBUILD_EXIT_CODE: 0 +- ERRORS: 0 +- NULLABLE-BASELINE-WARNINGS: 0 +- CSC_OUT_TASKMASTER: 2 +- CSC_OUT_TASKMASTER_TEST: 2 +- WRITESET_DIAGNOSTIC_LINES: 0 +- TEST_DLL_EXISTS: True +- Verdict: PASS (no NULLABLE BASELINE NOT CLEAN). MSBuild resolved through vswhere; solution passed by absolute path; file log at coverage\logs\p0-t12.msbuild.log (git-ignored, not copied). diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index dace8475b..34fffc1b5 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -831,34 +831,34 @@ At baseline only the main file exists, so `TryInvokeSink` and `BuildNotifyFailed - [x] [P0-T3] Verify the base anchor of the branch for `TaskMaster/` and `TaskMaster.Test/` against BASE-SHA and record FEATURE/evidence/baseline/anchor-production.md (this task creates the file; P0-T4 appends to it). - Command: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse origin/main`; `git -C WORKTREE merge-base 94287369908cc920b21b0e3256314f988ad7d2f5 HEAD`; `git -C WORKTREE diff --exit-code --stat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test`. - Acceptance, all required: the merge-base output equals `94287369908cc920b21b0e3256314f988ad7d2f5` (otherwise `BASE NOT ANCESTOR`: stop); the diff exits 0 and prints nothing (`ANCHOR-CODE-DIFF-EXIT=0`; otherwise `CODE DIFFERS FROM BASE`: stop); `HEAD:` and `ORIGIN-MAIN:` are recorded as observations, and when `ORIGIN-MAIN:` differs from BASE-SHA the artifact records `ORIGIN-MAIN MOVED` without stopping, because every anchor in this plan is the BASE-SHA literal, except the MERGE-SHA anchor of P2-T8 (version 1.4). -- [ ] [P0-T4] Verify the split anchors and the false-before token and phrase values of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, and append them to FEATURE/evidence/baseline/anchor-production.md. +- [x] [P0-T4] Verify the split anchors and the false-before token and phrase values of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, and append them to FEATURE/evidence/baseline/anchor-production.md. - Command: the Read tool over `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` lines 1 to 10, 43 to 55, 253 to 272, 403 to 407, 433 to 442 and 492 to 496; `CMD-STRIPPED-COUNT` with `TOKENS-STRUCT`; `CMD-PHRASE-COUNT` with `PHRASES-DOC`; the Grep tool count of pattern `^` over the file. - Acceptance, all required: the Grep count is 496; the lines read match fact 2 exactly at 3 (`using System.Globalization;`), 4 (`using System.Threading;`), 45, 46, 47 (`/// `), 51, 52 (blank), 53, 255 (` }`), 256 (blank), 257 (`/// `), 270, 405, 435 (` }`), 436 (blank), 437 (`/// `), 440, 494 (` }`), 495 (` }`) and 496 (`}`) (any mismatch is `SPLIT ANCHOR MOVED`: stop); every `STRIPPED` value equals its base value in `TOKENS-STRUCT` and every `PHRASE` value equals its base value in `PHRASES-DOC` (any other value is `BASELINE TOKEN MISMATCH`: stop). The recorded values are the false-before half of the P1-T22 gate. -- [ ] [P0-T5] Re-derive the test-side anchors for TaskMaster.Test/TaskMaster.Test.csproj, TaskMaster/TaskMaster.csproj and the fixture partials under TaskMaster.Test/Ribbon, and record FEATURE/evidence/baseline/anchor-test-side.md. +- [x] [P0-T5] Re-derive the test-side anchors for TaskMaster.Test/TaskMaster.Test.csproj, TaskMaster/TaskMaster.csproj and the fixture partials under TaskMaster.Test/Ribbon, and record FEATURE/evidence/baseline/anchor-test-side.md. - Command: `CMD-LINECOUNT`; the Grep tool with `-n` over `TaskMaster.Test/TaskMaster.Test.csproj` for `EngineToggleStateCoordinatorTests` and over `TaskMaster/TaskMaster.csproj` for `EngineToggleStateCoordinator`; the Grep tool count over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests*.cs` for `\[TestMethod\]`, `\[DataTestMethod\]`, `\[DataRow\(`, `\[TestClass\]` and `OnNotify`; the Grep tool over `TaskMaster.Test` for each `NEW-NAMES-964` name. - Acceptance, all required: `PRODUCTION-FILES: 1` and `TEST-PARTIALS: 6`, with `LINES` 496 for the production file and 470, 77, 175, 277, 290 and 215 for the primary, PrimeFaultOrdering, PrimeRegistration, Race, RepeatFaultSuppression and ThrowingSink partials; the test csproj lists six fixture entries (352, 359 to 363) and the production csproj one coordinator entry (466); `[TestMethod]` totals 36, `[DataTestMethod]` 1, `[DataRow(` 3 and `[TestClass]` 1, recorded as `EXPECTED-CASES: 39`; `OnNotify` count 0; every `NEW-NAMES-964` name has 0 hits; no file named `EngineToggleStateCoordinatorTests.SinkGuard.cs`, `EngineToggleStateCoordinator.Prime.cs` or `EngineToggleStateCoordinator.Messages.cs` exists. Any mismatch is `TEST ANCHOR MOVED`: stop. -- [ ] [P0-T6] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record FEATURE/evidence/baseline/bootstrap-sdk.md. +- [x] [P0-T6] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record FEATURE/evidence/baseline/bootstrap-sdk.md. - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version'` - Acceptance: `SDK_MARKER=True`, `dotnet --version` prints a version string rather than the global.json error message, `EXIT_CODE: 0`. Installer lines carrying an absolute path are transcribed with REDACTED-PATH. -- [ ] [P0-T7] Restore the manifest tools from dotnet-tools.json with `dotnet tool restore` and record FEATURE/evidence/baseline/bootstrap-tool-restore.md. +- [x] [P0-T7] Restore the manifest tools from dotnet-tools.json with `dotnet tool restore` and record FEATURE/evidence/baseline/bootstrap-tool-restore.md. - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool restore; "RESTORE_EXIT=$LASTEXITCODE"; dotnet tool list --local'` - Acceptance: `RESTORE_EXIT=0` and the local tool list contains a row whose Package Id is `csharpier` and whose Version is `1.2.6`. Only the Package Id and Version columns are transcribed (the Manifest column carries an absolute path). -- [ ] [P0-T8] Restore NuGet packages with scripts/vscode/Invoke-Restore.ps1 and record FEATURE/evidence/baseline/bootstrap-nuget-restore.md. +- [x] [P0-T8] Restore NuGet packages with scripts/vscode/Invoke-Restore.ps1 and record FEATURE/evidence/baseline/bootstrap-nuget-restore.md. - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; $env:MSBUILDDISABLENODEREUSE = "1"; & (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1"); "RESTORE_EXIT=$LASTEXITCODE"; "PACKAGE_DIRS=$(@(Get-ChildItem -LiteralPath packages -Directory -ErrorAction SilentlyContinue).Count)"; foreach ($proj in @("TaskMaster\TaskMaster.csproj", "TaskMaster.Test\TaskMaster.Test.csproj")) { $dir = Split-Path -Parent $proj; [xml]$x = Get-Content -LiteralPath $proj -Raw; $missing = @($x.SelectNodes("//*[local-name()=""Analyzer""]") | Where-Object { -not (Test-Path -LiteralPath (Join-Path $dir $_.GetAttribute("Include"))) }).Count; "ANALYZER_MISSING $proj = $missing" }'` - Acceptance: `RESTORE_EXIT=0`, `PACKAGE_DIRS=` at least 1, and both `ANALYZER_MISSING` values 0 (non-zero is `ANALYZER PATH SKEW`: stop). -- [ ] [P0-T9] Provision the dotnet-coverage global tool (guarded) for scripts/vscode/Invoke-MSTestWithCoverage.ps1 and record FEATURE/evidence/baseline/bootstrap-dotnet-coverage.md. +- [x] [P0-T9] Provision the dotnet-coverage global tool (guarded) for scripts/vscode/Invoke-MSTestWithCoverage.ps1 and record FEATURE/evidence/baseline/bootstrap-dotnet-coverage.md. - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; if (-not (Get-Command dotnet-coverage -ErrorAction SilentlyContinue)) { dotnet tool install --global dotnet-coverage }; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"; dotnet-coverage --version'` - Acceptance: `DOTNET_COVERAGE_RESOLVED=True`, a version line is printed, `EXIT_CODE: 0`. -- [ ] [P0-T10] Capture the read-only formatter baseline over the worktree (`.csharpierignore` applies) with `dotnet tool run csharpier check .` and record FEATURE/evidence/baseline/csharpier-check-baseline.md. +- [x] [P0-T10] Capture the read-only formatter baseline over the worktree (`.csharpierignore` applies) with `dotnet tool run csharpier check .` and record FEATURE/evidence/baseline/csharpier-check-baseline.md. - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'` - Acceptance: `EXIT_CODE:` is the printed `CSHARPIER_EXIT_CODE:` value, the `Checked N files` line is recorded, and every path CSharpier reports as unformatted is listed. `EXIT_CODE: 0` is required; a non-zero value is `FORMAT BASELINE NOT CLEAN`: stop, because the Phase 2 repository-wide format would then rewrite files outside the Write Set. -- [ ] [P0-T11] Capture the analyzer baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p0-t11) and record FEATURE/evidence/baseline/msbuild-analyzer-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). +- [x] [P0-T11] Capture the analyzer baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p0-t11) and record FEATURE/evidence/baseline/msbuild-analyzer-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded as `ANALYZER-BASELINE-WARNINGS:`; `TEST_DLL_EXISTS: True`. Non-zero exit is `ANALYZER BASELINE NOT CLEAN`: stop. -- [ ] [P0-T12] Capture the nullable baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p0-t12) and record FEATURE/evidence/baseline/msbuild-nullable-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). +- [x] [P0-T12] Capture the nullable baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p0-t12) and record FEATURE/evidence/baseline/msbuild-nullable-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded as `NULLABLE-BASELINE-WARNINGS:`; `TEST_DLL_EXISTS: True`. Non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop. -- [ ] [P0-T13] Capture the pre-change coordinator fixture run over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll with `CMD-VSTEST` (`TASKID` p0-t13, `NAMES` `NAMES-ALL`) and record FEATURE/evidence/baseline/coordinator-tests-baseline.md. +- [x] [P0-T13] Capture the pre-change coordinator fixture run over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll with `CMD-VSTEST` (`TASKID` p0-t13, `NAMES` `NAMES-ALL`) and record FEATURE/evidence/baseline/coordinator-tests-baseline.md. - Acceptance, all required: `EXIT_CODE: 0`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `COUNTERS` with `total` equal to `EXPECTED-CASES` (39) and `failed=0`, recorded as `BASELINE-TOTAL: 39`; every `INVARIANT-NAMES` entry has a `RESULT ... = Passed` line; no `RESULT` line names a `NEW-NAMES-964` entry; no `FAILED` line. Anything else is `EXISTING FIXTURE NOT GREEN AT BASE`: stop. -- [ ] [P0-T14] Capture the baseline repository-wide test-and-coverage run with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` (`STAGE` baseline) and then `CMD-COVERAGE-POST` (`STAGE` baseline, `RAW` per its rule), and record FEATURE/evidence/baseline/coverage-baseline.md. +- [x] [P0-T14] Capture the baseline repository-wide test-and-coverage run with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` (`STAGE` baseline) and then `CMD-COVERAGE-POST` (`STAGE` baseline, `RAW` per its rule), and record FEATURE/evidence/baseline/coverage-baseline.md. - Artifact: `Timestamp:`; `Command:` `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`; `EXIT_CODE:` the `RUNNER_EXIT_CODE:`; `ExpectedExitCode:` equal to it when non-zero; `Output Summary:` (at most 20 lines) carrying the exit code, `RAW:`, `LINE-FLOOR:`, `BRANCH-FLOOR:`, the `First-party coverage:` line (repository line and branch headline), the `ROOT` line, `COORD-LINES`, `COORD-LINE-RATE:` (the coordinator class line coverage baseline, recorded also as `BASELINE-COORD-LINE-RATE:`) and `BASELINE-FAILED-FQN-COUNT:` (the `FAILED-FQN-COUNT:` value) in `Output Summary:` and every `FAILED-FQN` row, with `TEST-DEFINITIONS:`, under `Details:`; then `Details:` with `DISCOVERED_LINE:`, `THRESHOLD_MESSAGE:`, `COLLECT_FAILURE_MESSAGE:`, `DOCUMENT_PRESENT:`, `TRX_PRESENT:`, `SUMMARY_FILE_PRESENT:`, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the summary verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, every `COORD-FILE` row, `COORD-CLASS-NODES:`, `COORD-BRANCHES`, and every `METHOD` and `METHOD-LINE` row. - Branches, checked in order: (d) `DOCUMENT_PRESENT: False` or `TRX_PRESENT: False` is `COVERAGE RUN ABORTED`: stop without `CMD-COVERAGE-POST`. (c) a non-empty `THRESHOLD_MESSAGE:`, `LINE-FLOOR: NOT MET` or `BRANCH-FLOOR: NOT MET` is `COVERAGE FLOOR BASELINE NOT MET`: record and stop. (b) a non-zero exit with a non-empty `COLLECT_FAILURE_MESSAGE:`, `TEST-DEFINITIONS:` at least 1, `FAILED-FQN-COUNT:` at least 1, no `FAILED-FQN` value beginning `UNRESOLVED:` and none beginning `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.` completes this task with those `FAILED-FQN` rows as the baseline failed set (the pre-existing local failures, for example the shell-icon test of fact 11; CI runs them). (a) exit 0 with both floors met completes it with `BASELINE-FAILED-FQN-COUNT: 0`. Any other outcome, including a non-zero exit with `FAILED-FQN-COUNT: 0` or with a `FAILED-FQN` value beginning `UNRESOLVED:`, is `COVERAGE RUN ABORTED`: stop. - Acceptance, all required: branch (a) or (b); `COORD-CLASS-NODES: 1` with the main-file `COORD-FILE` row `nodes=1` and the Prime and Messages rows `nodes=0`; `COORD-LINES` valid at least 1; `METHOD HandleToggleClickAsync` and `METHOD CompletePrime` rows present with `elements=` at least 1, recorded as `BASELINE-METHOD-HTC-UNCOVERED:` and `BASELINE-METHOD-CP-UNCOVERED:`; `METHOD TryInvokeSink ABSENT` and `METHOD BuildNotifyFailedMessage ABSENT`; the projection contains a `package` named `TaskMaster` with `LINE` and `BRANCH` counters; the summary first line begins `Test run outcome:`; no absolute path in the artifact. `coverage\baseline-964.cobertura.xml` and `coverage\baseline-964.trx` stay on disk, git-ignored. From 1b0304f8883abf66dedffad9f8aeb6c6c8e887d2 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 07:46:21 -0400 Subject: [PATCH 05/17] refactor(964): split EngineToggleStateCoordinator into three partials Behaviour-preserving move of the prime members to EngineToggleStateCoordinator.Prime.cs and the message helpers to EngineToggleStateCoordinator.Messages.cs (P1-T1 to P1-T8). The split census admits only the declaration keyword, scaffolds and redistributed usings; the unchanged fixture passes 39 of 39. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../EngineToggleStateCoordinator.Messages.cs | 73 +++++ .../EngineToggleStateCoordinator.Prime.cs | 190 +++++++++++++ .../Ribbon/EngineToggleStateCoordinator.cs | 249 +----------------- TaskMaster/TaskMaster.csproj | 2 + .../evidence/other/implementation-handoff.md | 25 ++ .../regression-testing/split-census.md | 51 ++++ .../regression-testing/split-fixture-green.md | 29 ++ .../plan.2026-10-02T05-20.md | 16 +- 8 files changed, 379 insertions(+), 256 deletions(-) create mode 100644 TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs create mode 100644 TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/implementation-handoff.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-census.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-fixture-green.md diff --git a/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs new file mode 100644 index 000000000..ff1d25933 --- /dev/null +++ b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs @@ -0,0 +1,73 @@ +using System; +using System.Globalization; + +namespace TaskMaster +{ + internal sealed partial class EngineToggleStateCoordinator + { + /// + /// Rendered in place of an engine key when the caller supplied null or empty, so a message + /// is never ambiguous about which key was seen. + /// + private const string NullEngineNameToken = "(null)"; + + /// + /// Renders an engine key for inclusion in a message, so a null key is never ambiguous. + /// + private static string RenderEngineName(string engineName) + { + return string.IsNullOrEmpty(engineName) ? NullEngineNameToken : engineName; + } + + /// + /// The message emitted when a toggle click is refused because the engines are unavailable. + /// + private static string BuildUnavailableMessage(string engineName) + { + return string.Format( + CultureInfo.CurrentCulture, + "The engine '{0}' is not available yet, so its enable/disable setting cannot be " + + "changed. Please try again once initialization completes.", + RenderEngineName(engineName) + ); + } + + /// + /// The message logged when the toggle path faults. + /// + private static string BuildToggleFailedMessage(string engineName) + { + return string.Format( + CultureInfo.CurrentCulture, + "Toggling the enable/disable setting for engine '{0}' failed.", + RenderEngineName(engineName) + ); + } + + /// + /// The message logged when the state prime faults. + /// + private static string BuildPrimeFailedMessage(string engineName) + { + return string.Format( + CultureInfo.CurrentCulture, + "Reading the activation state for engine '{0}' failed; its toggle continues to " + + "report unchecked. Further failures of this kind for this engine are not " + + "logged again.", + RenderEngineName(engineName) + ); + } + + /// + /// The message carried by the for an unmapped engine key. + /// + private static string BuildUnmappedKeyMessage(string engineName) + { + return string.Format( + CultureInfo.CurrentCulture, + "The engine key '{0}' has no toggle checkbox in EngineToggleCatalog.", + RenderEngineName(engineName) + ); + } + } +} diff --git a/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs new file mode 100644 index 000000000..36bf8c923 --- /dev/null +++ b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs @@ -0,0 +1,190 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using UtilitiesCS; + +namespace TaskMaster +{ + internal sealed partial class EngineToggleStateCoordinator + { + /// + /// The in-flight — or most recently completed — prime for an engine key, exposed so tests + /// can await the prime deterministically instead of polling or sleeping. + /// + /// The engine key; ordinal, case-sensitive. + /// + /// The prime task, or when no prime has been started for + /// the key. The returned task never faults: a prime fault is observed inside the prime + /// itself and reported through logError. For a key whose prime did not run to + /// completion, the marker is cleared only after that report has returned or thrown, so a + /// caller that receives can rely on the report having + /// been attempted or deliberately suppressed as a repeat of a kind already reported. + /// + internal Task GetPrimeTask(string engineName) + { + if (string.IsNullOrEmpty(engineName)) + { + return Task.CompletedTask; + } + + return _primeTasks.TryGetValue(engineName, out var prime) ? prime : Task.CompletedTask; + } + + /// + /// Starts the single prime for an engine key, unless one is already registered or the + /// engines are not yet available. + /// + private void StartPrimeIfNeeded(string engineName, string controlId) + { + var engines = _enginesAccessor(); + if (engines is null) + { + return; + } + + lock (_primeGate) + { + if (_primeTasks.ContainsKey(engineName)) + { + return; + } + + // Registration precedes the start (issue #944): a prime can complete on any + // thread, including before StartObservedPrime returns, and it must always find + // its own marker to remove; registering afterwards let a finished prime's + // removal run first and leave a stale marker that blocked every later re-prime. + var marker = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously + ); + _primeTasks[engineName] = marker.Task; + StartObservedPrime(engines, engineName, controlId, marker); + } + } + + /// + /// Runs and attaches the fault observer. + /// + /// + /// The observer is a continuation rather than a catch clause. The three + /// catch clauses in this type all sit in and + /// : the click boundary and the two sink guards. Reading + /// inside marks the fault + /// observed, so no unobserved task remains. The continuation task itself is discarded; + /// the value a test awaits is the marker, which the continuation completes only through + /// SetResult in a finally after exits, so it + /// never faults or cancels. Because also contains a failure + /// of the sink, the discarded continuation has no remaining throw source of its own. + /// + private void StartObservedPrime( + IAppItemEngines engines, + string engineName, + string controlId, + TaskCompletionSource marker + ) + { + _ = ApplyPrimeAsync(engines, engineName, controlId) + .ContinueWith( + completed => + { + try + { + CompletePrime(completed, engineName); + } + finally + { + marker.SetResult(true); + } + }, + CancellationToken.None, + TaskContinuationOptions.None, + TaskScheduler.Default + ); + } + + /// + /// Reads the real activation state once, stores it, and invalidates the mapped control. + /// Contains no catch: a fault propagates into the returned task, where + /// observes it. + /// + private async Task ApplyPrimeAsync( + IAppItemEngines engines, + string engineName, + string controlId + ) + { + // The ticket is taken immediately before the activation read, so a prime whose + // observation began before a toggle's cannot overwrite the toggle's newer result. + var sequence = _pressedState.NextSequence(); + var active = await engines.EngineActiveAsync(engineName).ConfigureAwait(false); + + if (_pressedState.TryApplyState(engineName, active, sequence)) + { + _invalidateControl(controlId); + } + } + + /// + /// Observes the outcome of a prime. On any outcome other than ran-to-completion the cache + /// is left unset — so the key still reports unchecked — the failure is reported through + /// logError unless the same failure kind was already reported for this engine, a + /// sink failure is contained here, and only then is the marker cleared for a later re-prime. + /// + /// + /// + /// The status is tested rather than the exception. A CANCELED task carries a null + /// , so a handler keyed on the exception returned early for a + /// cancellation: nothing was logged, the cache stayed unset, and the in-flight marker stayed + /// registered, which blocked any re-prime for the rest of the session. When there is no + /// exception to unwrap a is synthesized so the sink + /// always receives one. The faulted path is unchanged and still reports the unwrapped base + /// exception. + /// + /// + /// The sink call is guarded (issue #947). The sink is the last reporting channel of this + /// type, so a failure inside it has nowhere else to go; letting it escape skipped the clear + /// below, which left a stale marker that blocked every later re-prime, and faulted the + /// discarded continuation unobserved. The guard follows + /// RibbonCommandBoundary.SafeLog. With the sink contained, the continuation in + /// has no remaining throw source of its own, so it + /// completes rather than faulting. + /// + /// + /// Repeat suppression (issue #948): each pair of engine key and base-exception type is + /// reported once, then recorded in by the statement + /// directly after the sink call, so a sink that throws leaves the report owed. Moving + /// that record before the sink, or into a catch or finally arm, suppresses it for the session. + /// + /// + private void CompletePrime(Task completed, string engineName) + { + if (completed.Status == TaskStatus.RanToCompletion) + { + return; + } + + var failure = + (Exception)completed.Exception?.GetBaseException() + ?? new TaskCanceledException(completed); + + // Report-then-clear is load-bearing: the marker stays registered until the + // report (if any) has returned or thrown, so a caller that observes the marker absent, + // including one that fetched the prime handle after the fault, is guaranteed the report + // has already been attempted or was deliberately skipped as an already reported kind. + var reportKey = (EngineName: engineName, FaultType: failure.GetType()); + if (!_reportedPrimeFaults.ContainsKey(reportKey)) + { + try + { + _logError(BuildPrimeFailedMessage(engineName), failure); + _reportedPrimeFaults[reportKey] = 0; + } + catch (Exception) + { + // Intentionally discarded: see the remarks on this method. + } + } + + _primeTasks.TryRemove(engineName, out _); + } + } +} diff --git a/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs b/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs index 598a2a6a7..3c3b36978 100644 --- a/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs +++ b/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Globalization; -using System.Threading; using System.Threading.Tasks; using UtilitiesCS; @@ -42,14 +40,8 @@ namespace TaskMaster /// UtilitiesCS.UiThread.Dispatcher. /// /// - internal sealed class EngineToggleStateCoordinator + internal sealed partial class EngineToggleStateCoordinator { - /// - /// Rendered in place of an engine key when the caller supplied null or empty, so a message - /// is never ambiguous about which key was seen. - /// - private const string NullEngineNameToken = "(null)"; - private readonly Func _enginesAccessor; private readonly Action _invalidateControl; private readonly Action _notifyUnavailable; @@ -253,244 +245,5 @@ internal async Task ExecuteToggleAsync(string engineName) _invalidateControl(controlId); } } - - /// - /// The in-flight — or most recently completed — prime for an engine key, exposed so tests - /// can await the prime deterministically instead of polling or sleeping. - /// - /// The engine key; ordinal, case-sensitive. - /// - /// The prime task, or when no prime has been started for - /// the key. The returned task never faults: a prime fault is observed inside the prime - /// itself and reported through logError. For a key whose prime did not run to - /// completion, the marker is cleared only after that report has returned or thrown, so a - /// caller that receives can rely on the report having - /// been attempted or deliberately suppressed as a repeat of a kind already reported. - /// - internal Task GetPrimeTask(string engineName) - { - if (string.IsNullOrEmpty(engineName)) - { - return Task.CompletedTask; - } - - return _primeTasks.TryGetValue(engineName, out var prime) ? prime : Task.CompletedTask; - } - - /// - /// Starts the single prime for an engine key, unless one is already registered or the - /// engines are not yet available. - /// - private void StartPrimeIfNeeded(string engineName, string controlId) - { - var engines = _enginesAccessor(); - if (engines is null) - { - return; - } - - lock (_primeGate) - { - if (_primeTasks.ContainsKey(engineName)) - { - return; - } - - // Registration precedes the start (issue #944): a prime can complete on any - // thread, including before StartObservedPrime returns, and it must always find - // its own marker to remove; registering afterwards let a finished prime's - // removal run first and leave a stale marker that blocked every later re-prime. - var marker = new TaskCompletionSource( - TaskCreationOptions.RunContinuationsAsynchronously - ); - _primeTasks[engineName] = marker.Task; - StartObservedPrime(engines, engineName, controlId, marker); - } - } - - /// - /// Runs and attaches the fault observer. - /// - /// - /// The observer is a continuation rather than a catch clause. The three - /// catch clauses in this type all sit in and - /// : the click boundary and the two sink guards. Reading - /// inside marks the fault - /// observed, so no unobserved task remains. The continuation task itself is discarded; - /// the value a test awaits is the marker, which the continuation completes only through - /// SetResult in a finally after exits, so it - /// never faults or cancels. Because also contains a failure - /// of the sink, the discarded continuation has no remaining throw source of its own. - /// - private void StartObservedPrime( - IAppItemEngines engines, - string engineName, - string controlId, - TaskCompletionSource marker - ) - { - _ = ApplyPrimeAsync(engines, engineName, controlId) - .ContinueWith( - completed => - { - try - { - CompletePrime(completed, engineName); - } - finally - { - marker.SetResult(true); - } - }, - CancellationToken.None, - TaskContinuationOptions.None, - TaskScheduler.Default - ); - } - - /// - /// Reads the real activation state once, stores it, and invalidates the mapped control. - /// Contains no catch: a fault propagates into the returned task, where - /// observes it. - /// - private async Task ApplyPrimeAsync( - IAppItemEngines engines, - string engineName, - string controlId - ) - { - // The ticket is taken immediately before the activation read, so a prime whose - // observation began before a toggle's cannot overwrite the toggle's newer result. - var sequence = _pressedState.NextSequence(); - var active = await engines.EngineActiveAsync(engineName).ConfigureAwait(false); - - if (_pressedState.TryApplyState(engineName, active, sequence)) - { - _invalidateControl(controlId); - } - } - - /// - /// Observes the outcome of a prime. On any outcome other than ran-to-completion the cache - /// is left unset — so the key still reports unchecked — the failure is reported through - /// logError unless the same failure kind was already reported for this engine, a - /// sink failure is contained here, and only then is the marker cleared for a later re-prime. - /// - /// - /// - /// The status is tested rather than the exception. A CANCELED task carries a null - /// , so a handler keyed on the exception returned early for a - /// cancellation: nothing was logged, the cache stayed unset, and the in-flight marker stayed - /// registered, which blocked any re-prime for the rest of the session. When there is no - /// exception to unwrap a is synthesized so the sink - /// always receives one. The faulted path is unchanged and still reports the unwrapped base - /// exception. - /// - /// - /// The sink call is guarded (issue #947). The sink is the last reporting channel of this - /// type, so a failure inside it has nowhere else to go; letting it escape skipped the clear - /// below, which left a stale marker that blocked every later re-prime, and faulted the - /// discarded continuation unobserved. The guard follows - /// RibbonCommandBoundary.SafeLog. With the sink contained, the continuation in - /// has no remaining throw source of its own, so it - /// completes rather than faulting. - /// - /// - /// Repeat suppression (issue #948): each pair of engine key and base-exception type is - /// reported once, then recorded in by the statement - /// directly after the sink call, so a sink that throws leaves the report owed. Moving - /// that record before the sink, or into a catch or finally arm, suppresses it for the session. - /// - /// - private void CompletePrime(Task completed, string engineName) - { - if (completed.Status == TaskStatus.RanToCompletion) - { - return; - } - - var failure = - (Exception)completed.Exception?.GetBaseException() - ?? new TaskCanceledException(completed); - - // Report-then-clear is load-bearing: the marker stays registered until the - // report (if any) has returned or thrown, so a caller that observes the marker absent, - // including one that fetched the prime handle after the fault, is guaranteed the report - // has already been attempted or was deliberately skipped as an already reported kind. - var reportKey = (EngineName: engineName, FaultType: failure.GetType()); - if (!_reportedPrimeFaults.ContainsKey(reportKey)) - { - try - { - _logError(BuildPrimeFailedMessage(engineName), failure); - _reportedPrimeFaults[reportKey] = 0; - } - catch (Exception) - { - // Intentionally discarded: see the remarks on this method. - } - } - - _primeTasks.TryRemove(engineName, out _); - } - - /// - /// Renders an engine key for inclusion in a message, so a null key is never ambiguous. - /// - private static string RenderEngineName(string engineName) - { - return string.IsNullOrEmpty(engineName) ? NullEngineNameToken : engineName; - } - - /// - /// The message emitted when a toggle click is refused because the engines are unavailable. - /// - private static string BuildUnavailableMessage(string engineName) - { - return string.Format( - CultureInfo.CurrentCulture, - "The engine '{0}' is not available yet, so its enable/disable setting cannot be " - + "changed. Please try again once initialization completes.", - RenderEngineName(engineName) - ); - } - - /// - /// The message logged when the toggle path faults. - /// - private static string BuildToggleFailedMessage(string engineName) - { - return string.Format( - CultureInfo.CurrentCulture, - "Toggling the enable/disable setting for engine '{0}' failed.", - RenderEngineName(engineName) - ); - } - - /// - /// The message logged when the state prime faults. - /// - private static string BuildPrimeFailedMessage(string engineName) - { - return string.Format( - CultureInfo.CurrentCulture, - "Reading the activation state for engine '{0}' failed; its toggle continues to " - + "report unchecked. Further failures of this kind for this engine are not " - + "logged again.", - RenderEngineName(engineName) - ); - } - - /// - /// The message carried by the for an unmapped engine key. - /// - private static string BuildUnmappedKeyMessage(string engineName) - { - return string.Format( - CultureInfo.CurrentCulture, - "The engine key '{0}' has no toggle checkbox in EngineToggleCatalog.", - RenderEngineName(engineName) - ); - } } } diff --git a/TaskMaster/TaskMaster.csproj b/TaskMaster/TaskMaster.csproj index 6a2f22a32..9000655b9 100644 --- a/TaskMaster/TaskMaster.csproj +++ b/TaskMaster/TaskMaster.csproj @@ -464,6 +464,8 @@ + + diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/implementation-handoff.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/implementation-handoff.md new file mode 100644 index 000000000..c42c9a54a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/implementation-handoff.md @@ -0,0 +1,25 @@ +# Implementation Handoff (P1-T1) + +Timestamp: 2026-10-03T07-42 +Task: P1-T1 +Plan: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md (version 1.5) + +Delegated executor role: atomic-executor, small-path implementation (Phase 1 executed task by task from the approved plan; no redesign). + +Write Set code paths (verbatim): +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (modify: split, then fix) +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (create) +- `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (create) +- `TaskMaster/TaskMaster.csproj` (modify: two compile items) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (modify: the `OnNotify` harness member) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (create) +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (modify: remark only, D-7a) +- `TaskMaster.Test/TaskMaster.Test.csproj` (modify: one compile item) + +Implementation-completion criteria (all must be met): +- P1-T24 pass-after gate: the coordinator fixture run against the fixed coordinator reports 43 total, 43 passed, 0 failed, every NEW-NAMES-964 and INVARIANT-NAMES entry Passed. +- P1-T22 census gate: every STRIPPED, PHRASE and SPAN-HASH value equals its required final value. +- P1-T25 unchanged-partials gate: the four untouched partials are byte-equal to BASE-SHA; Race.cs has no non-documentation change; the primary fixture removes exactly one line. +- P1-T26 size gate: every production file at most 450 lines, every test partial at most 500 lines, csproj registrations complete. + +Edit-route rule (D-11): every `.cs` and `.csproj` change is made with the Edit or Write tool, never through a shell write, so the repository hooks see it. A hook denial of any Edit or Write is reported verbatim and stops that step; the executor does not retry with a rephrased edit or another tool. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-census.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-census.md new file mode 100644 index 000000000..8f8d60a2c --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-census.md @@ -0,0 +1,51 @@ +# Split Census (P1-T5, P1-T6, P1-T7) + +## CSPROJ-REGISTRATION: (P1-T5) + +Timestamp: 2026-10-03T07-44 +Task: P1-T5 +Command: Grep tool counts over TaskMaster/TaskMaster.csproj for `Ribbon\x5CEngineToggleStateCoordinator\.cs"`, `Ribbon\x5CEngineToggleStateCoordinator\.Messages\.cs"`, `Ribbon\x5CEngineToggleStateCoordinator\.Prime\.cs"`; Read tool over lines 465-469; git diff --numstat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster/TaskMaster.csproj +EXIT_CODE: 0 + +Output Summary: +- Grep counts: `EngineToggleStateCoordinator.cs"` 1 (line 466), `EngineToggleStateCoordinator.Messages.cs"` 1 (line 467), `EngineToggleStateCoordinator.Prime.cs"` 1 (line 468). +- Read observation: the two new lines (467, 468) directly follow the existing entry at 466; line 469 is `Ribbon\RibbonController.cs`. +- Numstat: `2 0 TaskMaster/TaskMaster.csproj` (2 inserted, 0 deleted). +- Verdict: PASS. + +## FORMAT: (P1-T6) + +Timestamp: 2026-10-03T07-44 +Task: P1-T6 +Command: dotnet tool run csharpier format TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs; dotnet tool run csharpier check TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs +EXIT_CODE: 0 + +Output Summary: +- Format: `Formatted 3 files in 2865ms.` exit 0 (processed count, not an assertion). +- Check: `Checked 3 files in 1005ms.` exit 0, no path listed. +- Verdict: PASS. + +## CENSUS: (P1-T7) + +Timestamp: 2026-10-03T07-44 +Task: P1-T7 +Command: CMD-SPLIT-CENSUS (ordinal multiset of non-blank trimmed lines: `git show 94287369908cc920b21b0e3256314f988ad7d2f5:TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` against the three split files) +EXIT_CODE: 0 + +Output Summary: +- Exactly the eight admitted difference lines; no other MISSING or EXTRA line. +- BASE-LINES: 496 (WORK-LINES: 512). +- Verdict: PASS (split is a pure move; no SPLIT NOT A PURE MOVE). + +Details (output verbatim): +``` +EXTRA x4 :: { +EXTRA x4 :: } +MISSING x1 :: internal sealed class EngineToggleStateCoordinator +EXTRA x3 :: internal sealed partial class EngineToggleStateCoordinator +EXTRA x2 :: namespace TaskMaster +EXTRA x2 :: using System; +EXTRA x1 :: using System.Threading.Tasks; +EXTRA x1 :: using UtilitiesCS; +BASE-LINES: 496 WORK-LINES: 512 +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-fixture-green.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-fixture-green.md new file mode 100644 index 000000000..1295194f9 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-fixture-green.md @@ -0,0 +1,29 @@ +# Split Fixture Green (P1-T8) + +Timestamp: 2026-10-03T07-45 +Task: P1-T8 +Command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU"; vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\p1-t8" "/Logger:trx;LogFileName=p1-t8.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +- Build: MSBUILD_EXIT_CODE: 0; ERRORS: 0; TEST_DLL_ADVANCED: True; CSC_OUT_TASKMASTER: 2; CSC_OUT_TASKMASTER_TEST: 2 (the split files compiled). +- Test: VSTEST_EXIT_CODE: 0; TRX_PRESENT: True; SEQUENCE_FILES: 0. +- COUNTERS total=39 executed=39 passed=39 failed=0 (total equals BASELINE-TOTAL 39). +- All 11 INVARIANT-NAMES entries Passed; no FAILED line. +- Verdict: PASS (no SPLIT CHANGED BEHAVIOUR). + +Details (CMD-VSTEST output lines): +``` +COUNTERS total=39 executed=39 passed=39 failed=0 +RESULT GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly = Passed +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly = Passed +RESULT GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns = Passed +RESULT GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged = Passed +RESULT HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing = Passed +RESULT GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime = Passed +RESULT HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport = Passed +RESULT GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared = Passed +RESULT GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime = Passed +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 34fffc1b5..786924b85 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -867,21 +867,21 @@ At baseline only the main file exists, so `TryInvokeSink` and `BuildNotifyFailed Phase 1 is the constrained small-path implementation, executed task by task by the delegated executor. Ordering: the split (P1-T2 to P1-T8) lands and is proven behaviour-preserving before any test is written; the regression tests (P1-T9 to P1-T14) are recorded failing against the split but unfixed coordinator; the fix (P1-T15 to P1-T24) then turns them green. -- [ ] [P1-T1] Record the implementation handoff for the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md in FEATURE/evidence/other/implementation-handoff.md. +- [x] [P1-T1] Record the implementation handoff for the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md in FEATURE/evidence/other/implementation-handoff.md. - Acceptance: the artifact carries `Timestamp:`, names the delegated executor role (atomic-executor, small-path implementation), lists the eight Write Set code paths verbatim, states the implementation-completion criteria (P1-T24 pass-after gate, P1-T22 census gate, P1-T25 unchanged-partials gate and P1-T26 size gate all met), and records the Edit-route rule of D-11. -- [ ] [P1-T2] Create `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Write tool as delivered source S1, transcribing base lines 47 to 51 and 437 to 494 of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` verbatim (read with the Read tool). +- [x] [P1-T2] Create `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Write tool as delivered source S1, transcribing base lines 47 to 51 and 437 to 494 of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` verbatim (read with the Read tool). - Acceptance: the file exists; its first two lines are `using System;` and `using System.Globalization;`; the Grep tool counts `internal sealed partial class EngineToggleStateCoordinator` 1, `private const string NullEngineNameToken` 1 and `private static string BuildUnmappedKeyMessage` 1 in it. The transcription itself is proved by P1-T7. -- [ ] [P1-T3] Create `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Write tool as delivered source S2, transcribing base lines 257 to 435 of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` verbatim. +- [x] [P1-T3] Create `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Write tool as delivered source S2, transcribing base lines 257 to 435 of `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` verbatim. - Acceptance: the file exists; its usings are exactly `System`, `System.Threading`, `System.Threading.Tasks` and `UtilitiesCS`; the Grep tool counts `internal Task GetPrimeTask` 1 and `private void CompletePrime` 1 in it. -- [ ] [P1-T4] Reduce `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit or Write tool to the 249-line main partial of delivered source S3. +- [x] [P1-T4] Reduce `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit or Write tool to the 249-line main partial of delivered source S3. - Acceptance: the Grep tool counts 0 in the file for each of `using System\.Globalization;`, `using System\.Threading;`, `NullEngineNameToken = `, `internal Task GetPrimeTask` and `private static string RenderEngineName`, and 1 for each of `internal sealed partial class EngineToggleStateCoordinator` and `internal async Task ExecuteToggleAsync`; the Grep count of pattern `^` over the file is 249. -- [ ] [P1-T5] Register the two new files in `TaskMaster/TaskMaster.csproj` with the Edit tool as delivered source C1. +- [x] [P1-T5] Register the two new files in `TaskMaster/TaskMaster.csproj` with the Edit tool as delivered source C1. - Acceptance: the Grep tool counts exactly 1 line for each of `Ribbon\x5CEngineToggleStateCoordinator\.cs"`, `Ribbon\x5CEngineToggleStateCoordinator\.Messages\.cs"` and `Ribbon\x5CEngineToggleStateCoordinator\.Prime\.cs"` in the file, the two new lines directly follow the existing entry (Read tool), and the numstat row of `git -C WORKTREE diff --numstat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster/TaskMaster.csproj` reports 2 inserted and 0 deleted lines. The Grep counts, the Read observation and the numstat row are recorded under `CSPROJ-REGISTRATION:` in FEATURE/evidence/regression-testing/split-census.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). -- [ ] [P1-T6] Format the three coordinator files under TaskMaster/Ribbon with `dotnet tool run csharpier format TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs`, then verify with `dotnet tool run csharpier check TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (both wrapped per the Toolchain commands convention). +- [x] [P1-T6] Format the three coordinator files under TaskMaster/Ribbon with `dotnet tool run csharpier format TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs`, then verify with `dotnet tool run csharpier check TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (both wrapped per the Toolchain commands convention). - Acceptance: the format exits 0 (its `Formatted N files` line is a processed count, not an assertion); the success-case observation is the check run, which exits 0 and prints `Checked 3 files` with no path listed. Both outputs are recorded in FEATURE/evidence/regression-testing/split-census.md under `FORMAT:` (appended to the file P1-T5 created). -- [ ] [P1-T7] Prove the split is a pure move with `CMD-SPLIT-CENSUS` over the three files under TaskMaster/Ribbon and append the output to FEATURE/evidence/regression-testing/split-census.md. +- [x] [P1-T7] Prove the split is a pure move with `CMD-SPLIT-CENSUS` over the three files under TaskMaster/Ribbon and append the output to FEATURE/evidence/regression-testing/split-census.md. - Acceptance: the output consists of exactly these eight difference lines and the `BASE-LINES:` line: `MISSING x1 :: internal sealed class EngineToggleStateCoordinator`, `EXTRA x3 :: internal sealed partial class EngineToggleStateCoordinator`, `EXTRA x2 :: namespace TaskMaster`, `EXTRA x4 :: {`, `EXTRA x4 :: }`, `EXTRA x2 :: using System;`, `EXTRA x1 :: using System.Threading.Tasks;`, `EXTRA x1 :: using UtilitiesCS;`. Any other `MISSING` or `EXTRA` line is `SPLIT NOT A PURE MOVE`: correct the transcription with the Edit tool against the base lines and re-run P1-T6 and this task; after two failed corrections stop. `BASE-LINES: 496` is required. -- [ ] [P1-T8] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t8) and run the unchanged fixture against the split coordinator with `CMD-VSTEST` (`TASKID` p1-t8, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/split-fixture-green.md. +- [x] [P1-T8] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t8) and run the unchanged fixture against the split coordinator with `CMD-VSTEST` (`TASKID` p1-t8, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/split-fixture-green.md. - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `CSC_OUT_TASKMASTER:` at least 1 (the split files compiled); `VSTEST_EXIT_CODE: 0`, `SEQUENCE_FILES: 0`, `COUNTERS` total equal to `BASELINE-TOTAL` (39) with `failed=0`; every `INVARIANT-NAMES` entry `Passed`. Anything else is `SPLIT CHANGED BEHAVIOUR`: stop. - [ ] [P1-T9] Add the `OnNotify` harness member to `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` with the Edit tool as delivered source T1. - Acceptance: the Grep tool counts `internal Action OnNotify` 1, `OnNotify\?\.Invoke\(message\);` 1 and `Notifications\.Add\(message\),` 0 in the file, and `Notifications\.Add\(message\);` 1. From 9de37caf19d3cbe1c446ea5e28bd384fb8f39121 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 07:49:37 -0400 Subject: [PATCH 06/17] test(964): add refusal-path sink-guard regression tests (fail-before recorded) Adds the OnNotify harness hook, the SinkGuard partial with the four NEW-NAMES-964 tests and the Race.cs remark rewording (P1-T9 to P1-T14). Against the split but unfixed coordinator the three FAIL-BEFORE-NAMES fail with their required reasons; the issue 948 guard and all invariant tests pass. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../EngineToggleStateCoordinatorTests.Race.cs | 8 +- ...neToggleStateCoordinatorTests.SinkGuard.cs | 169 ++++++++++++++++++ .../EngineToggleStateCoordinatorTests.cs | 13 +- TaskMaster.Test/TaskMaster.Test.csproj | 1 + .../refusal-path-fail-before.md | 70 ++++++++ .../sink-guard-partial-tokens.md | 29 +++ .../plan.2026-10-02T05-20.md | 12 +- 7 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-fail-before.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/sink-guard-partial-tokens.md diff --git a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs index 4cb15ff85..973eb90e4 100644 --- a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs +++ b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs @@ -195,10 +195,10 @@ public async Task ExecuteToggleAsync_WithNullEngines_ThrowsInvalidOperationExcep /// /// Assertion order is load-bearing. The harness engines mock is strict and this test /// supplies one setup, so the re-prime triggered by the second read re-enters that same - /// canceled task and logs a second error. An error-count assertion taken after the re-prime - /// would therefore be unsatisfiable by construction. The single-error assertion is made - /// first, and the marker-cleared conclusion is drawn from prime-handle identity, which is - /// deterministic. + /// canceled task. Since issue #948 that second cancellation is a repeat of a kind already + /// reported and is not logged, but the single-error assertion is still made before the + /// re-prime so the test does not depend on the suppression rule. The marker-cleared + /// conclusion is drawn from prime-handle identity, which is deterministic. /// [TestMethod] public async Task GetPressed_WhenPrimeIsCanceled_LogsErrorAndClearsPrimeMarker() diff --git a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs new file mode 100644 index 000000000..03937f571 --- /dev/null +++ b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs @@ -0,0 +1,169 @@ +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace TaskMaster.Test.Ribbon +{ + /// + /// Regression tests for issue #964: on the refusal path of HandleToggleClickAsync, + /// taken when the engines accessor yields null, a notifyUnavailable sink that throws + /// must not escape into the async void Office handler, and its exception must be + /// reported once through logError; plus a guard for the issue #948 record placement + /// now that every sink call goes through one shared guard. A further partial of the + /// coordinator fixture, so the private Harness and LoggedError types and the + /// fixture constants are reused. The harness invokes OnNotify and OnLogError + /// after it has recorded the call, so a throwing hook both records the attempt and models a + /// throwing sink. No test sleeps, polls, reads the clock or touches the filesystem. + /// + public partial class EngineToggleStateCoordinatorTests + { + #region Issue #964 — a throwing notification sink on the refusal path + + /// + /// Regression for issue #964 and the test that carries the fail-before obligation. + /// Invariant: with the engines unavailable, a throwing notification sink does not escape + /// the click handler. Without the fix the exception escapes the unguarded notification + /// call, so the awaited call faults with it. + /// + [TestMethod] + public async Task HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow() + { + // Arrange: the pre-SetGlobals window, with a notification sink that throws. + var harness = new Harness { EnginesAvailable = false }; + harness.OnNotify = _ => throw new InvalidOperationException("notify sink failed"); + + // Act + Func act = () => harness.Coordinator.HandleToggleClickAsync(SpamEngine); + + // Assert + await act.Should() + .NotThrowAsync("a throwing notification sink must not escape the refusal path"); + } + + /// + /// Regression for issue #964, the reporting guarantee: the notification is attempted + /// once, its exception reaches the log sink once and unchanged, and the refused click + /// still touches no engine member and invalidates no control. Without the fix the + /// exception escapes, so the test method throws before any assertion runs. + /// + [TestMethod] + public async Task HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing() + { + // Arrange + var harness = new Harness { EnginesAvailable = false }; + var notifyFailure = new InvalidOperationException("notify sink failed"); + harness.OnNotify = _ => throw notifyFailure; + + // Act + await harness.Coordinator.HandleToggleClickAsync(SpamEngine); + + // Assert + harness + .Notifications.Should() + .ContainSingle("the notification sink is attempted exactly once"); + harness + .Errors.Should() + .ContainSingle("a notification failure is reported once through the log sink"); + harness + .Errors[0] + .Exception.Should() + .BeSameAs( + notifyFailure, + "the log sink receives the notification failure unchanged" + ); + harness.Errors[0].Message.Should().Contain(SpamEngine); + harness.Engines.VerifyNoOtherCalls(); + harness.Invalidations.Should().BeEmpty("a refused click changes no state to display"); + } + + /// + /// Regression for issue #964, both sinks failing: when the log sink also throws while it + /// reports the notification failure, the click handler still completes without throwing, + /// because no further reporting channel remains. Without the fix the notification + /// exception escapes first. + /// + [TestMethod] + public async Task HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow() + { + // Arrange + var harness = new Harness { EnginesAvailable = false }; + var notifyFailure = new InvalidOperationException("notify sink failed"); + harness.OnNotify = _ => throw notifyFailure; + harness.OnLogError = (_, _) => throw new InvalidOperationException("log sink failed"); + + // Act + Func act = () => harness.Coordinator.HandleToggleClickAsync(SpamEngine); + + // Assert + await act.Should().NotThrowAsync("the refusal path contains a failure of both sinks"); + harness.Notifications.Should().ContainSingle("the notification is attempted once"); + harness + .Errors.Should() + .ContainSingle("the log sink is attempted once before it throws"); + harness + .Errors[0] + .Exception.Should() + .BeSameAs( + notifyFailure, + "the log sink receives the notification failure unchanged" + ); + } + + #endregion Issue #964 — a throwing notification sink on the refusal path + + #region Issue #964 — the issue #948 record placement under the shared guard + + /// + /// Guard for the issue #948 invariant now that the prime-fault sink call goes through the + /// shared guard: a log sink that throws while a faulted prime is reported leaves that + /// failure kind unrecorded, so the next fault of the same kind is reported again. Passes + /// before and after the issue #964 change; it fails if the record moves ahead of the sink + /// call or out of the branch taken when the sink returned normally. + /// + [TestMethod] + public async Task GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain() + { + // Arrange: two faulted primes of one kind; the sink throws on the first report only. + var harness = new Harness(); + var firstProbe = new TaskCompletionSource(); + var secondProbe = new TaskCompletionSource(); + harness + .Engines.SetupSequence(x => x.EngineActiveAsync(SpamEngine)) + .Returns(firstProbe.Task) + .Returns(secondProbe.Task); + var reports = 0; + harness.OnLogError = (_, _) => + { + reports++; + if (reports == 1) + { + throw new InvalidOperationException("log sink failed"); + } + }; + harness.Coordinator.GetPressed(SpamEngine); + var firstPrime = harness.Coordinator.GetPrimeTask(SpamEngine); + + // Act + firstProbe.SetException(new InvalidOperationException("configuration load failed")); + await firstPrime; + harness.Coordinator.GetPressed(SpamEngine); + var secondPrime = harness.Coordinator.GetPrimeTask(SpamEngine); + secondProbe.SetException(new InvalidOperationException("configuration load failed")); + await secondPrime; + + // Assert + secondPrime.Should().NotBeSameAs(firstPrime, "the later read registered a new prime"); + harness + .Errors.Should() + .HaveCount( + 2, + "a report the throwing sink did not accept is still owed, so the repeat is reported" + ); + harness.Errors[1].Message.Should().Contain(SpamEngine); + } + + #endregion Issue #964 — the issue #948 record placement under the shared guard + } +} diff --git a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs index 57ff5b16b..c06f474fd 100644 --- a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs +++ b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs @@ -411,7 +411,11 @@ internal Harness() Invalidations.Add(controlId); OnInvalidate?.Invoke(controlId); }, - message => Notifications.Add(message), + message => + { + Notifications.Add(message); + OnNotify?.Invoke(message); + }, (message, exception) => { Errors.Add(new LoggedError(message, exception)); @@ -444,6 +448,13 @@ internal Harness() /// internal Action OnLogError { get; set; } + /// + /// An optional extra observer invoked from inside the notification sink, immediately + /// after the message has been appended to , so a throwing + /// hook both records the attempt and models a throwing notification sink. + /// + internal Action OnNotify { get; set; } + internal List Invalidations { get; } = new List(); internal List Notifications { get; } = new List(); diff --git a/TaskMaster.Test/TaskMaster.Test.csproj b/TaskMaster.Test/TaskMaster.Test.csproj index 1b9c0b285..00c1fec9a 100644 --- a/TaskMaster.Test/TaskMaster.Test.csproj +++ b/TaskMaster.Test/TaskMaster.Test.csproj @@ -361,6 +361,7 @@ + diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-fail-before.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-fail-before.md new file mode 100644 index 000000000..5f6108c00 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-fail-before.md @@ -0,0 +1,70 @@ +# Refusal Path Fail-Before (P1-T14, expect-fail) + +Timestamp: 2026-10-03T07-48 +Task: P1-T14 [expect-fail] +Command: vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\p1-t14" "/Logger:trx;LogFileName=p1-t14.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" (preceded by msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" and CMD-STRIPPED-COUNT with TOKENS-STRUCT) +EXIT_CODE: 1 +ExpectedExitCode: 1 + +Output Summary: +- Build: MSBUILD_EXIT_CODE: 0; ERRORS: 0; TEST_DLL_ADVANCED: True; CSC_OUT_TASKMASTER: 0; CSC_OUT_TASKMASTER_TEST: 2. +- Stripped census immediately before the run: every token at its base value except the two split tokens (`internalsealedclassEngineToggleStateCoordinator` 0, `internalsealedpartialclassEngineToggleStateCoordinator` 3); the fix is absent from the coordinator under test. +- VSTEST_EXIT_CODE: 1; TRX_PRESENT: True; SEQUENCE_FILES: 0. +- COUNTERS total=43 executed=43 passed=40 failed=3 (BASELINE-TOTAL 39 plus 4). +- FAILED lines name exactly the three FAIL-BEFORE-NAMES. +- GUARD-NAME `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` = Passed; all 11 INVARIANT-NAMES Passed. +- Reason gate: each failure message carries its two required fragments (see Details). +- Verdict: PASS for the expect-fail task (no FAIL-BEFORE NOT REPRODUCED, FAIL-BEFORE WRONG REASON, INVARIANT GUARD RED AT BASE or UNEXPECTED FAILURE). + +Details: + +Stripped census (immediately before the test run): +``` +STRIPPED [catch(] = 3 +STRIPPED [catch(Exceptionex)] = 1 +STRIPPED [catch(Exception)] = 2 +STRIPPED [TryInvokeSink(] = 0 +STRIPPED [_logError(] = 2 +STRIPPED [_notifyUnavailable(] = 1 +STRIPPED [TryInvokeSink(()=>_notifyUnavailable(BuildUnavailableMessage(engineName)),outvarnotifyFailure)] = 0 +STRIPPED [TryInvokeSink(()=>_logError(BuildNotifyFailedMessage(engineName),notifyFailure),out_)] = 0 +STRIPPED [TryInvokeSink(()=>_logError(BuildToggleFailedMessage(engineName),ex),out_)] = 0 +STRIPPED [failure),out_)){_reportedPrimeFaults[reportKey]=0;}] = 0 +STRIPPED [_reportedPrimeFaults[reportKey]=0;] = 1 +STRIPPED [privatestaticboolTryInvokeSink(ActionsinkCall,outExceptionsinkFailure)] = 0 +STRIPPED [privatestaticstringBuildNotifyFailedMessage(stringengineName)] = 0 +STRIPPED [internalsealedclassEngineToggleStateCoordinator] = 0 +STRIPPED [internalsealedpartialclassEngineToggleStateCoordinator] = 3 +STRIPPED [_primeTasks.TryRemove(engineName,out_);] = 1 +``` + +CMD-VSTEST output (absolute paths replaced with REDACTED-PATH): +``` +COUNTERS total=43 executed=43 passed=40 failed=3 +RESULT GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly = Passed +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain = Passed +RESULT HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow = Failed +RESULT HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing = Passed +RESULT GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly = Passed +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow = Failed +RESULT GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged = Passed +RESULT GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime = Passed +RESULT GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns = Passed +RESULT GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared = Passed +RESULT HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport = Passed +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing = Failed +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime = Passed +FAILED HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow +MESSAGE HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow :: Did not expect any exception because the refusal path contains a failure of both sinks, but found System.InvalidOperationException: notify sink failed / at TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.<>c__DisplayClass45_0.b__0(String _) in REDACTED-PATH\TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs:line 93 / at TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.Harness.<.ctor>b__0_2(String message) in REDACTED-PATH\TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs:line 417 / at TaskMaster.EngineToggleStateCoordinator.d__10.MoveNext() in REDACTED-PATH\TaskMaster\Ribbon\EngineToggleStateCoordinator.cs:line 178 / --- End of stack trace from previous location where exception was thrown --- / at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() / at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) / at FluentAssertions.Specialized.NonGenericAsyncFunctionAssertions.d__4.MoveNext() in /_/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs:line 101. +FAILED HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow +MESSAGE HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow :: Did not expect any exception because a throwing notification sink must not escape the refusal path, but found System.InvalidOperationException: notify sink failed / at TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.<>c.b__43_0(String _) in REDACTED-PATH\TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs:line 35 / at TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.Harness.<.ctor>b__0_2(String message) in REDACTED-PATH\TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs:line 417 / at TaskMaster.EngineToggleStateCoordinator.d__10.MoveNext() in REDACTED-PATH\TaskMaster\Ribbon\EngineToggleStateCoordinator.cs:line 178 / --- End of stack trace from previous location where exception was thrown --- / at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() / at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) / at FluentAssertions.Specialized.NonGenericAsyncFunctionAssertions.d__4.MoveNext() in /_/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs:line 101. +FAILED HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing +MESSAGE HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing :: Test method TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing threw exception: / System.InvalidOperationException: notify sink failed +``` + +Reason gate: +- `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow`: contains `a throwing notification sink must not escape the refusal path` and `notify sink failed`. +- `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing`: contains `threw exception` and `notify sink failed`. +- `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`: contains `the refusal path contains a failure of both sinks` and `notify sink failed`. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/sink-guard-partial-tokens.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/sink-guard-partial-tokens.md new file mode 100644 index 000000000..b97cdd51d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/sink-guard-partial-tokens.md @@ -0,0 +1,29 @@ +# SinkGuard Partial Registration and Tokens (P1-T11, P1-T13) + +## TEST-CSPROJ-REGISTRATION: (P1-T11) + +Timestamp: 2026-10-03T07-47 +Task: P1-T11 +Command: Grep tool count over TaskMaster.Test/TaskMaster.Test.csproj for `Ribbon\x5CEngineToggleStateCoordinatorTests\.SinkGuard\.cs"`; Read tool over lines 362-365; git diff --numstat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster.Test/TaskMaster.Test.csproj +EXIT_CODE: 0 + +Output Summary: +- Grep count: 1 line (line 364). +- Read observation: line 364 directly follows the RepeatFaultSuppression entry at 363; line 365 is `Ribbon\EngineTogglePressedStateCacheTests.cs`. +- Numstat: `1 0 TaskMaster.Test/TaskMaster.Test.csproj` (1 inserted, 0 deleted). +- Verdict: PASS. + +## FORMAT-AND-TOKENS: (P1-T13) + +Timestamp: 2026-10-03T07-47 +Task: P1-T13 +Command: dotnet tool run csharpier format TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs; dotnet tool run csharpier check (same three paths); Grep tool counts over TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +EXIT_CODE: 0 + +Output Summary: +- Format: `Formatted 3 files in 2931ms.` exit 0 (processed count). +- Check: `Checked 3 files in 1066ms.` exit 0, no path listed. +- SinkGuard partial banned-token counts: `Thread\.Sleep` 0, `Task\.Delay` 0, `DoNotParallelize` 0, `GetTempPath` 0, `File\.` 0, `DateTime\.Now` 0, `DateTime\.UtcNow` 0 (one alternation over all seven: 0 matches). +- Positive control: `TaskCompletionSource` 2 (lines 130, 131). +- Reason fragments, each whole on one physical line: `a throwing notification sink must not escape the refusal path` 1 (line 42); `the refusal path contains a failure of both sinks` 1 (line 100); `notify sink failed` 3 (lines 35, 56, 92). +- Verdict: PASS. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 786924b85..cba3bdb18 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -883,17 +883,17 @@ Phase 1 is the constrained small-path implementation, executed task by task by t - Acceptance: the output consists of exactly these eight difference lines and the `BASE-LINES:` line: `MISSING x1 :: internal sealed class EngineToggleStateCoordinator`, `EXTRA x3 :: internal sealed partial class EngineToggleStateCoordinator`, `EXTRA x2 :: namespace TaskMaster`, `EXTRA x4 :: {`, `EXTRA x4 :: }`, `EXTRA x2 :: using System;`, `EXTRA x1 :: using System.Threading.Tasks;`, `EXTRA x1 :: using UtilitiesCS;`. Any other `MISSING` or `EXTRA` line is `SPLIT NOT A PURE MOVE`: correct the transcription with the Edit tool against the base lines and re-run P1-T6 and this task; after two failed corrections stop. `BASE-LINES: 496` is required. - [x] [P1-T8] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t8) and run the unchanged fixture against the split coordinator with `CMD-VSTEST` (`TASKID` p1-t8, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/split-fixture-green.md. - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `CSC_OUT_TASKMASTER:` at least 1 (the split files compiled); `VSTEST_EXIT_CODE: 0`, `SEQUENCE_FILES: 0`, `COUNTERS` total equal to `BASELINE-TOTAL` (39) with `failed=0`; every `INVARIANT-NAMES` entry `Passed`. Anything else is `SPLIT CHANGED BEHAVIOUR`: stop. -- [ ] [P1-T9] Add the `OnNotify` harness member to `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` with the Edit tool as delivered source T1. +- [x] [P1-T9] Add the `OnNotify` harness member to `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` with the Edit tool as delivered source T1. - Acceptance: the Grep tool counts `internal Action OnNotify` 1, `OnNotify\?\.Invoke\(message\);` 1 and `Notifications\.Add\(message\),` 0 in the file, and `Notifications\.Add\(message\);` 1. -- [ ] [P1-T10] Create `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` with the Write tool as delivered source T2. +- [x] [P1-T10] Create `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` with the Write tool as delivered source T2. - Acceptance: the file exists and the Grep tool counts `\[TestMethod\]` 4 and `\[TestClass\]` 0 in it, and each `NEW-NAMES-964` name exactly once. -- [ ] [P1-T11] Register the new partial in `TaskMaster.Test/TaskMaster.Test.csproj` with the Edit tool as delivered source C2. +- [x] [P1-T11] Register the new partial in `TaskMaster.Test/TaskMaster.Test.csproj` with the Edit tool as delivered source C2. - Acceptance: the Grep tool counts exactly 1 line for `Ribbon\x5CEngineToggleStateCoordinatorTests\.SinkGuard\.cs"` in the file, directly after the RepeatFaultSuppression entry (Read tool), and the numstat row of `git -C WORKTREE diff --numstat 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster.Test/TaskMaster.Test.csproj` reports 1 inserted and 0 deleted lines. The Grep count, the Read observation and the numstat row are recorded under `TEST-CSPROJ-REGISTRATION:` in FEATURE/evidence/regression-testing/sink-guard-partial-tokens.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). -- [ ] [P1-T12] Reword the stale remark in `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` with the Edit tool as delivered source T3 (related defect D-7a). +- [x] [P1-T12] Reword the stale remark in `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` with the Edit tool as delivered source T3 (related defect D-7a). - Acceptance: the Grep tool counts `logs a second error` 0 and `Since issue #948 that second cancellation is a repeat of a kind already` 1 in the file; P1-T25 proves no code line changed. -- [ ] [P1-T13] Format the three touched test files under TaskMaster.Test/Ribbon with `dotnet tool run csharpier format TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs`, verify with `dotnet tool run csharpier check` over the same three paths (both wrapped per the Toolchain commands convention), and append to FEATURE/evidence/regression-testing/sink-guard-partial-tokens.md. +- [x] [P1-T13] Format the three touched test files under TaskMaster.Test/Ribbon with `dotnet tool run csharpier format TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs`, verify with `dotnet tool run csharpier check` over the same three paths (both wrapped per the Toolchain commands convention), and append to FEATURE/evidence/regression-testing/sink-guard-partial-tokens.md. - Acceptance, all required: the check run exits 0 and prints `Checked 3 files` with no path listed (the success-case observation of the write-mode format); in the SinkGuard partial the Grep tool counts 0 for each of `Thread\.Sleep`, `Task\.Delay`, `DoNotParallelize`, `GetTempPath`, `File\.`, `DateTime\.Now` and `DateTime\.UtcNow`, and at least 2 for `TaskCompletionSource` (positive control that the file was read); the reason fragments `a throwing notification sink must not escape the refusal path`, `the refusal path contains a failure of both sinks` and `notify sink failed` each occur whole on one physical line (Grep count at least 1 each). -- [ ] [P1-T14] [expect-fail] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t14) and run the fixture with the new tests against the split but unfixed coordinator with `CMD-VSTEST` (`TASKID` p1-t14, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/refusal-path-fail-before.md with `ExpectedExitCode:` equal to the observed non-zero exit. +- [x] [P1-T14] [expect-fail] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t14) and run the fixture with the new tests against the split but unfixed coordinator with `CMD-VSTEST` (`TASKID` p1-t14, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/refusal-path-fail-before.md with `ExpectedExitCode:` equal to the observed non-zero exit. - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_TASKMASTER_TEST:` at least 1; `CMD-STRIPPED-COUNT` with `TOKENS-STRUCT`, run immediately before the test run, prints the base value for every token except the two split tokens (`"internalsealedclassEngineToggleStateCoordinator"` 0 and `"internalsealedpartialclassEngineToggleStateCoordinator"` 3), proving the fix is absent from the coordinator under test; `SEQUENCE_FILES: 0`; `COUNTERS` total equal to `BASELINE-TOTAL` plus 4 (43) with `failed=3`; the `FAILED` lines name exactly the three `FAIL-BEFORE-NAMES`; `RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain = Passed` and every `INVARIANT-NAMES` entry `Passed`. Reason gate: the first name's `MESSAGE` contains both `a throwing notification sink must not escape the refusal path` and `notify sink failed`; the second's contains `threw exception` and `notify sink failed`; the third's contains `the refusal path contains a failure of both sinks` and `notify sink failed`. A `FAIL-BEFORE-NAMES` entry that passes is `FAIL-BEFORE NOT REPRODUCED`: stop; one whose message lacks its two fragments is `FAIL-BEFORE WRONG REASON`: stop; the `GUARD-NAME` failing is `INVARIANT GUARD RED AT BASE`: stop; any other failed test is `UNEXPECTED FAILURE`: stop. Absolute paths in messages are transcribed as REDACTED-PATH. - [ ] [P1-T15] Apply delivered source F1 (constructor parameter docs) and F2 (`GetPressed` returns) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. - Acceptance: the Grep tool counts `and must not throw: its` 1, `an exception it throws is reported once through` 1, `Receives an observed prime fault, toggle fault or notification failure as a message` 1 and `honours its non-throwing precondition` 1 in the file. From ca215e0683cbd3f10995272e1188e590a4405201 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 07:52:02 -0400 Subject: [PATCH 07/17] fix(964): guard every coordinator sink call through TryInvokeSink Applies delivered sources F1 to F8 (P1-T15 to P1-T21): the refusal-path notification is guarded and its failure is logged once through the guarded log sink; one TryInvokeSink helper holds the only sink catch; CompletePrime records a reported fault kind only on the success branch of the guard; documentation corrected. P1-T22 halted pending maintainer approval. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../EngineToggleStateCoordinator.Messages.cs | 13 +++ .../EngineToggleStateCoordinator.Prime.cs | 75 +++++++------ .../Ribbon/EngineToggleStateCoordinator.cs | 103 +++++++++++++----- .../qa-gates/production-edit-scope.md | 21 ++++ .../plan.2026-10-02T05-20.md | 14 +-- 5 files changed, 160 insertions(+), 66 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md diff --git a/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs index ff1d25933..1f6aaaa97 100644 --- a/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs +++ b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs @@ -44,6 +44,19 @@ private static string BuildToggleFailedMessage(string engineName) ); } + /// + /// The message logged when the notification for a refused toggle click throws. + /// + private static string BuildNotifyFailedMessage(string engineName) + { + return string.Format( + CultureInfo.CurrentCulture, + "Notifying that the engine '{0}' is not available failed, so the refused toggle " + + "click was not surfaced to the user.", + RenderEngineName(engineName) + ); + } + /// /// The message logged when the state prime faults. /// diff --git a/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs index 36bf8c923..2d64af928 100644 --- a/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs +++ b/TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs @@ -8,17 +8,21 @@ namespace TaskMaster internal sealed partial class EngineToggleStateCoordinator { /// - /// The in-flight — or most recently completed — prime for an engine key, exposed so tests - /// can await the prime deterministically instead of polling or sleeping. + /// The registration marker for an engine key, exposed so tests can await the outcome of + /// its prime deterministically instead of polling or sleeping. The marker is not the + /// prime task itself: it is registered before the prime starts and is completed only after + /// the prime outcome has been observed and, on a fault or cancellation, reported. /// /// The engine key; ordinal, case-sensitive. /// - /// The prime task, or when no prime has been started for - /// the key. The returned task never faults: a prime fault is observed inside the prime - /// itself and reported through logError. For a key whose prime did not run to - /// completion, the marker is cleared only after that report has returned or thrown, so a - /// caller that receives can rely on the report having - /// been attempted or deliberately suppressed as a repeat of a kind already reported. + /// The registered marker, or when no marker is registered + /// for the key. The marker never faults or cancels: a prime fault is observed by + /// and reported through logError, and the marker is + /// completed in a finally after that observation. For a key whose prime did not + /// run to completion, the marker is cleared only after that report has returned or + /// thrown, so a caller that receives can rely on the + /// report having been attempted or deliberately suppressed as a repeat of a kind already + /// reported. /// internal Task GetPrimeTask(string engineName) { @@ -65,15 +69,16 @@ private void StartPrimeIfNeeded(string engineName, string controlId) /// Runs and attaches the fault observer. /// /// - /// The observer is a continuation rather than a catch clause. The three - /// catch clauses in this type all sit in and - /// : the click boundary and the two sink guards. Reading - /// inside marks the fault - /// observed, so no unobserved task remains. The continuation task itself is discarded; - /// the value a test awaits is the marker, which the continuation completes only through - /// SetResult in a finally after exits, so it - /// never faults or cancels. Because also contains a failure - /// of the sink, the discarded continuation has no remaining throw source of its own. + /// The observer is a continuation rather than a catch clause. The two + /// catch clauses in this type are the click boundary in + /// and the single sink guard in + /// . Reading inside + /// marks the fault observed, so no unobserved task remains. + /// The continuation task itself is discarded; the value a test awaits is the marker, + /// which the continuation completes only through SetResult in a finally + /// after exits, so it never faults or cancels. Because + /// routes its sink call through , + /// the discarded continuation has no remaining throw source of its own. /// private void StartObservedPrime( IAppItemEngines engines, @@ -127,7 +132,8 @@ string controlId /// Observes the outcome of a prime. On any outcome other than ran-to-completion the cache /// is left unset — so the key still reports unchecked — the failure is reported through /// logError unless the same failure kind was already reported for this engine, a - /// sink failure is contained here, and only then is the marker cleared for a later re-prime. + /// sink failure is contained by , and only then is the marker + /// cleared for a later re-prime. /// /// /// @@ -140,19 +146,20 @@ string controlId /// exception. /// /// - /// The sink call is guarded (issue #947). The sink is the last reporting channel of this - /// type, so a failure inside it has nowhere else to go; letting it escape skipped the clear - /// below, which left a stale marker that blocked every later re-prime, and faulted the - /// discarded continuation unobserved. The guard follows - /// RibbonCommandBoundary.SafeLog. With the sink contained, the continuation in - /// has no remaining throw source of its own, so it - /// completes rather than faulting. + /// The sink call is guarded (issue #947) through , the guard + /// this type uses at every sink call site (issue #964). The sink is the last reporting + /// channel of this type, so a failure inside it has nowhere else to go; letting it escape + /// skipped the clear below, which left a stale marker that blocked every later re-prime, + /// and faulted the discarded continuation unobserved. With the sink contained, the + /// continuation in has no remaining throw source of its + /// own, so it completes rather than faulting. /// /// /// Repeat suppression (issue #948): each pair of engine key and base-exception type is - /// reported once, then recorded in by the statement - /// directly after the sink call, so a sink that throws leaves the report owed. Moving - /// that record before the sink, or into a catch or finally arm, suppresses it for the session. + /// reported once, then recorded in by the only + /// statement of the branch taken when reports that the sink + /// returned normally, so a sink that throws leaves the report owed. Moving that record + /// before the sink call, or out of that branch, suppresses it for the session. /// /// private void CompletePrime(Task completed, string engineName) @@ -173,15 +180,15 @@ private void CompletePrime(Task completed, string engineName) var reportKey = (EngineName: engineName, FaultType: failure.GetType()); if (!_reportedPrimeFaults.ContainsKey(reportKey)) { - try + if ( + TryInvokeSink( + () => _logError(BuildPrimeFailedMessage(engineName), failure), + out _ + ) + ) { - _logError(BuildPrimeFailedMessage(engineName), failure); _reportedPrimeFaults[reportKey] = 0; } - catch (Exception) - { - // Intentionally discarded: see the remarks on this method. - } } _primeTasks.TryRemove(engineName, out _); diff --git a/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs b/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs index 3c3b36978..29134295d 100644 --- a/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs +++ b/TaskMaster/Ribbon/EngineToggleStateCoordinator.cs @@ -85,9 +85,11 @@ private readonly ConcurrentDictionary< /// Creates a coordinator over an engines accessor and three injected sinks. /// /// - /// Supplies the current engines container. Must not be null, but is expected to return - /// null before the ribbon controller has been given its globals, which this type treats as - /// "state unknown" rather than as an error. + /// Supplies the current engines container. Must not be null, and must not throw: its + /// result is read outside any guard by and by the refusal check + /// of , so an exception it raised would escape both. + /// It is expected to return null before the ribbon controller has been given its globals, + /// which this type treats as "state unknown" rather than as an error. /// /// /// Receives a ribbon control id whenever the cached state behind that control changes, so @@ -95,11 +97,14 @@ private readonly ConcurrentDictionary< /// /// /// Receives exactly one message per toggle click refused because the engines are not - /// available. Presentation is the sink's concern. Must not be null. + /// available. Presentation is the sink's concern. Must not be null. The call is guarded + /// (issue #964): an exception it throws is reported once through + /// and is not rethrown. /// /// - /// Receives an observed prime or toggle fault as a message plus the exception. Must not be - /// null. + /// Receives an observed prime fault, toggle fault or notification failure as a message + /// plus the exception. Must not be null. The call is guarded: an exception it throws is + /// discarded, because no further reporting channel remains. /// /// Any argument is null. internal EngineToggleStateCoordinator( @@ -126,7 +131,8 @@ Action logError /// /// The cached activation state, or when the key is null, /// whitespace, unmapped, or has never been primed. This method performs a dictionary read - /// only: it never awaits, never blocks, and never throws. + /// only: it never awaits, never blocks, and never throws while the engines accessor + /// honours its non-throwing precondition. /// /// /// On a cache miss with the engines available, at most one prime per key is started; a @@ -153,7 +159,8 @@ internal bool GetPressed(string engineName) /// /// The toggle-click boundary: the only catch clause in this type that observes an - /// engine fault. The other two are sink guards, here and in . + /// engine fault. Every sink call on this path goes through , + /// which holds the only other catch clause. /// /// The engine key whose activation setting is being flipped. /// @@ -162,20 +169,35 @@ internal bool GetPressed(string engineName) /// /// /// When the engines are not available the click is refused with exactly one - /// notifyUnavailable message and nothing else is invoked. Otherwise - /// runs inside a single boundary try/catch: - /// a fault is reported through logError, is not rethrown, and does not invalidate. - /// The sink call is itself guarded (issue #947): the sink is the last reporting channel, - /// so a failure inside it has nowhere else to go and is discarded deliberately, following - /// RibbonCommandBoundary.SafeLog. This method therefore never throws, even when the - /// sink throws, because its caller is an async void Office handler whose faults - /// would otherwise become unobserved. + /// notifyUnavailable message and no engine member is invoked. That notification is + /// guarded (issue #964): if the sink throws, its exception is reported once through + /// logError. Otherwise runs inside a single + /// boundary try/catch: a fault is reported through logError, is not + /// rethrown, and does not invalidate. Every logError call is itself guarded + /// (issue #947): it is the last reporting channel, so a failure inside it has nowhere + /// else to go and is discarded deliberately, following + /// RibbonCommandBoundary.SafeLog. This method therefore never throws on either + /// path, even when both sinks throw, provided the engines accessor honours its + /// non-throwing precondition, because its caller is an async void Office handler + /// whose faults would otherwise become unobserved. /// internal async Task HandleToggleClickAsync(string engineName) { if (_enginesAccessor() is null) { - _notifyUnavailable(BuildUnavailableMessage(engineName)); + if ( + !TryInvokeSink( + () => _notifyUnavailable(BuildUnavailableMessage(engineName)), + out var notifyFailure + ) + ) + { + _ = TryInvokeSink( + () => _logError(BuildNotifyFailedMessage(engineName), notifyFailure), + out _ + ); + } + return; } @@ -185,14 +207,7 @@ internal async Task HandleToggleClickAsync(string engineName) } catch (Exception ex) { - try - { - _logError(BuildToggleFailedMessage(engineName), ex); - } - catch (Exception) - { - // Intentionally discarded: see the remarks on this method. - } + _ = TryInvokeSink(() => _logError(BuildToggleFailedMessage(engineName), ex), out _); } } @@ -245,5 +260,43 @@ internal async Task ExecuteToggleAsync(string engineName) _invalidateControl(controlId); } } + + /// + /// Invokes one injected sink and contains any exception it throws (issues #947 and #964). + /// This holds the only catch clause in this type that intercepts a sink failure; + /// every notifyUnavailable and logError call goes through it. + /// + /// The sink invocation, with its arguments already bound. + /// + /// The exception the sink threw, or when the sink returned + /// normally. + /// + /// + /// when the sink returned normally; when + /// it threw. The exception is never rethrown. + /// + /// + /// The caller decides what happens to a contained failure, following + /// RibbonCommandBoundary.ReportFailure: the refusal path of + /// forwards a notification failure to the log sink, + /// and every log-sink caller discards a log failure because no further channel remains. + /// records a reported fault kind only when this method + /// returns , so a sink that throws leaves the report owed + /// (issue #948). + /// + private static bool TryInvokeSink(Action sinkCall, out Exception sinkFailure) + { + try + { + sinkCall(); + sinkFailure = null; + return true; + } + catch (Exception ex) + { + sinkFailure = ex; + return false; + } + } } } diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md new file mode 100644 index 000000000..3f706ae67 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md @@ -0,0 +1,21 @@ +# Production Edit Scope (P1-T21, P1-T22) + +## FORMAT: (P1-T21) + +Timestamp: 2026-10-03T07-51 +Task: P1-T21 +Command: dotnet tool run csharpier format TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs; dotnet tool run csharpier check TaskMaster/Ribbon/EngineToggleStateCoordinator.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs; Grep tool count over TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs for `private static string BuildNotifyFailedMessage\(string engineName\)` +EXIT_CODE: 0 + +Output Summary: +- F8 inserted directly after the closing brace of `BuildToggleFailedMessage`. +- Grep count `private static string BuildNotifyFailedMessage\(string engineName\)` in the Messages file: 1. +- Format: `Formatted 3 files in 2766ms.` exit 0 (processed count). +- Check: `Checked 3 files in 1021ms.` exit 0, no path listed. +- Verdict: PASS. + +## P1-T22 — HALTED PENDING MAINTAINER APPROVAL + +Timestamp: 2026-10-03T07-51 +Task: P1-T22 +Status: HALTED PENDING APPROVAL. No P1-T22 command (CMD-STRIPPED-COUNT, CMD-PHRASE-COUNT, CMD-PROTECTED-SPANS) has been run. Per the coordinator's binding instruction, execution stopped before P1-T22 so that a separate maintainer approval can be obtained (the CMD-PHRASE-COUNT payload with PHRASES-DOC was blocked at P0-T4 by `.claude/hooks/enforce-promotion-mcp-only.ps1`, and the 2026-10-03 one-time bypass covered P0-T4 only). The task remains unchecked in the plan; its census results will be appended to this file when it is executed. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index cba3bdb18..9b336a03c 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -895,19 +895,19 @@ Phase 1 is the constrained small-path implementation, executed task by task by t - Acceptance, all required: the check run exits 0 and prints `Checked 3 files` with no path listed (the success-case observation of the write-mode format); in the SinkGuard partial the Grep tool counts 0 for each of `Thread\.Sleep`, `Task\.Delay`, `DoNotParallelize`, `GetTempPath`, `File\.`, `DateTime\.Now` and `DateTime\.UtcNow`, and at least 2 for `TaskCompletionSource` (positive control that the file was read); the reason fragments `a throwing notification sink must not escape the refusal path`, `the refusal path contains a failure of both sinks` and `notify sink failed` each occur whole on one physical line (Grep count at least 1 each). - [x] [P1-T14] [expect-fail] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t14) and run the fixture with the new tests against the split but unfixed coordinator with `CMD-VSTEST` (`TASKID` p1-t14, `NAMES` `NAMES-ALL`), recording FEATURE/evidence/regression-testing/refusal-path-fail-before.md with `ExpectedExitCode:` equal to the observed non-zero exit. - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_TASKMASTER_TEST:` at least 1; `CMD-STRIPPED-COUNT` with `TOKENS-STRUCT`, run immediately before the test run, prints the base value for every token except the two split tokens (`"internalsealedclassEngineToggleStateCoordinator"` 0 and `"internalsealedpartialclassEngineToggleStateCoordinator"` 3), proving the fix is absent from the coordinator under test; `SEQUENCE_FILES: 0`; `COUNTERS` total equal to `BASELINE-TOTAL` plus 4 (43) with `failed=3`; the `FAILED` lines name exactly the three `FAIL-BEFORE-NAMES`; `RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain = Passed` and every `INVARIANT-NAMES` entry `Passed`. Reason gate: the first name's `MESSAGE` contains both `a throwing notification sink must not escape the refusal path` and `notify sink failed`; the second's contains `threw exception` and `notify sink failed`; the third's contains `the refusal path contains a failure of both sinks` and `notify sink failed`. A `FAIL-BEFORE-NAMES` entry that passes is `FAIL-BEFORE NOT REPRODUCED`: stop; one whose message lacks its two fragments is `FAIL-BEFORE WRONG REASON`: stop; the `GUARD-NAME` failing is `INVARIANT GUARD RED AT BASE`: stop; any other failed test is `UNEXPECTED FAILURE`: stop. Absolute paths in messages are transcribed as REDACTED-PATH. -- [ ] [P1-T15] Apply delivered source F1 (constructor parameter docs) and F2 (`GetPressed` returns) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. +- [x] [P1-T15] Apply delivered source F1 (constructor parameter docs) and F2 (`GetPressed` returns) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. - Acceptance: the Grep tool counts `and must not throw: its` 1, `an exception it throws is reported once through` 1, `Receives an observed prime fault, toggle fault or notification failure as a message` 1 and `honours its non-throwing precondition` 1 in the file. -- [ ] [P1-T16] Apply delivered source F3 (`HandleToggleClickAsync` summary, remarks and body) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. +- [x] [P1-T16] Apply delivered source F3 (`HandleToggleClickAsync` summary, remarks and body) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool. - Acceptance: the Grep tool counts `_notifyUnavailable\(BuildUnavailableMessage\(engineName\)\);` 0 and `The other two are sink guards` 0 in the file, and `out var notifyFailure` 1. -- [ ] [P1-T17] Insert delivered source F4 (`TryInvokeSink`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool, directly after the closing brace of `ExecuteToggleAsync`. +- [x] [P1-T17] Insert delivered source F4 (`TryInvokeSink`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` with the Edit tool, directly after the closing brace of `ExecuteToggleAsync`. - Acceptance: the Grep tool counts `private static bool TryInvokeSink\(Action sinkCall, out Exception sinkFailure\)` 1 in the file. -- [ ] [P1-T18] Apply delivered source F5 (`GetPrimeTask` documentation) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. +- [x] [P1-T18] Apply delivered source F5 (`GetPrimeTask` documentation) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. - Acceptance: the Grep tool counts `The in-flight` 0 and `The prime task, or` 0 in the file, and `The marker is not the` at least 1. -- [ ] [P1-T19] Apply delivered source F6 (`StartObservedPrime` remarks) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. +- [x] [P1-T19] Apply delivered source F6 (`StartObservedPrime` remarks) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. - Acceptance: the Grep tool counts `The three` 0 and `all sit in` 0 in the file. -- [ ] [P1-T20] Apply delivered source F7a, F7b and F7c (`CompletePrime` summary, remarks and guarded report) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. The record `_reportedPrimeFaults[reportKey] = 0;` must be the only statement of the branch taken when `TryInvokeSink` returns `true`, directly after the guarded sink call; it is never placed before the call, in an `else` branch or after the branch (D-3, the #948 invariant that a throwing sink leaves the report owed). +- [x] [P1-T20] Apply delivered source F7a, F7b and F7c (`CompletePrime` summary, remarks and guarded report) to `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` with the Edit tool. The record `_reportedPrimeFaults[reportKey] = 0;` must be the only statement of the branch taken when `TryInvokeSink` returns `true`, directly after the guarded sink call; it is never placed before the call, in an `else` branch or after the branch (D-3, the #948 invariant that a throwing sink leaves the report owed). - Acceptance: the Grep tool counts `catch \(Exception\)` 0, `sink failure is contained here` 0 and `_reportedPrimeFaults\[reportKey\] = 0;` 1 in the file. -- [ ] [P1-T21] Insert delivered source F8 (`BuildNotifyFailedMessage`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Edit tool, directly after the closing brace of `BuildToggleFailedMessage`, then format the three coordinator files under TaskMaster/Ribbon with the P1-T6 format command and verify them with the P1-T6 check command (both wrapped per the Toolchain commands convention). +- [x] [P1-T21] Insert delivered source F8 (`BuildNotifyFailedMessage`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Edit tool, directly after the closing brace of `BuildToggleFailedMessage`, then format the three coordinator files under TaskMaster/Ribbon with the P1-T6 format command and verify them with the P1-T6 check command (both wrapped per the Toolchain commands convention). - Acceptance: the Grep tool counts `private static string BuildNotifyFailedMessage\(string engineName\)` 1 in the Messages file; the check run exits 0 and prints `Checked 3 files` with no path listed (the success-case observation of the write-mode format). Both outputs and the Grep count are recorded under `FORMAT:` in FEATURE/evidence/qa-gates/production-edit-scope.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). - [ ] [P1-T22] Verify the edit scope of the three files under TaskMaster/Ribbon (AC3, AC5, AC7) with `CMD-STRIPPED-COUNT` (`TOKENS-STRUCT`), `CMD-PHRASE-COUNT` (`PHRASES-DOC`) and `CMD-PROTECTED-SPANS` (`SIGNATURES-PROTECTED`), and append to FEATURE/evidence/qa-gates/production-edit-scope.md. - Acceptance, all required: every `STRIPPED` value equals its required final value in `TOKENS-STRUCT`; every `PHRASE` value equals its required final value in `PHRASES-DOC`; every `SPAN-HASH` line reads `equal=True` (twelve signatures and two field declarations). Together with the P0-T4 base values this shows each token and phrase moving from its false-before to its true-after value. The artifact also records, from reading the formatted files with the Read tool, the line ranges of the two remaining `catch` clauses (one in `HandleToggleClickAsync`, one in `TryInvokeSink`) as `CATCH-SITES:`. Any mismatch is `EDIT SCOPE MISMATCH`: correct the edit against the delivered source with the Edit tool and re-run P1-T21 and this task; after two failed corrections stop. From fc1624489862e481414fc659e02f8adb1f4eab94 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 08:08:36 -0400 Subject: [PATCH 08/17] docs(964): record P1-T22 to P1-T26 edit-scope, pass-after, test-diff and line-count evidence Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../evidence/qa-gates/file-line-counts.md | 53 +++++++++++ .../qa-gates/production-edit-scope.md | 93 ++++++++++++++++++- .../qa-gates/test-partials-unchanged.md | 29 ++++++ .../refusal-path-pass-after.md | 53 +++++++++++ .../plan.2026-10-02T05-20.md | 10 +- 5 files changed, 232 insertions(+), 6 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/file-line-counts.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/test-partials-unchanged.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-pass-after.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/file-line-counts.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/file-line-counts.md new file mode 100644 index 000000000..fa9a7cd16 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/file-line-counts.md @@ -0,0 +1,53 @@ +# File Line Counts and Registrations (P1-T26, AC6) + +Timestamp: 2026-10-03T08-07 +Task: P1-T26 +Command: CMD-LINECOUNT; CMD-HASH; Grep tool count of `Ribbon\x5CEngineToggleStateCoordinator\.` over TaskMaster/TaskMaster.csproj; Grep tool count of `Ribbon\x5CEngineToggleStateCoordinatorTests` over TaskMaster.Test/TaskMaster.Test.csproj +EXIT_CODE: 0 + +Output Summary: +- PRODUCTION-FILES: 3; production LINES 302 (main), 197 (Prime), 86 (Messages); every value at most 450 (plan expectation about 307, 196 and 86 was an observation only). +- TEST-PARTIALS: 7; test LINES 481 (primary), 77, 175, 277 (Race), 290, 169 (SinkGuard), 215; every value at most 500. +- TaskMaster/TaskMaster.csproj: 3 coordinator compile entries (lines 466, 467, 468), one per production LINES row. +- TaskMaster.Test/TaskMaster.Test.csproj: 7 fixture entries (lines 352, 359 to 364), one per test LINES row. +- Verdict: PASS (no FILE SIZE CEILING EXCEEDED). + +CMD-LINECOUNT output: +``` +LINES TaskMaster\Ribbon\EngineToggleStateCoordinator.cs = 302 +LINES TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs = 86 +LINES TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs = 197 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs = 481 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.PrimeFaultOrdering.cs = 77 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.PrimeRegistration.cs = 175 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.Race.cs = 277 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.RepeatFaultSuppression.cs = 290 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs = 169 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.ThrowingSink.cs = 215 +PRODUCTION-FILES: 3 +TEST-PARTIALS: 7 +``` + +Csproj registrations (Grep tool, matching lines): +``` +TaskMaster/TaskMaster.csproj:466: +TaskMaster/TaskMaster.csproj:467: +TaskMaster/TaskMaster.csproj:468: +TaskMaster.Test/TaskMaster.Test.csproj:352: +TaskMaster.Test/TaskMaster.Test.csproj:359: +TaskMaster.Test/TaskMaster.Test.csproj:360: +TaskMaster.Test/TaskMaster.Test.csproj:361: +TaskMaster.Test/TaskMaster.Test.csproj:362: +TaskMaster.Test/TaskMaster.Test.csproj:363: +TaskMaster.Test/TaskMaster.Test.csproj:364: +``` + +PHASE1-HASHES: +``` +HASH TaskMaster\Ribbon\EngineToggleStateCoordinator.cs = 8836AEFB84FA685EAD4EFB993C3A0E1492EB21606A06CF0B2FE1497E872E4EF4 +HASH TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs = 50DC74A866D6310184EA89C2773875994D9C73EEB3DF11B63244A80B80AC52CD +HASH TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs = 1E52B69D775038726117DEB0A63AEAC301A13E2FA5B9C96E6B00CC63046159AC +HASH TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs = 3E905F281D4493F289783EDE8BC9C98127547349CDDBB52E5D6DC3D692604211 +HASH TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs = D172DA1C8E2596198AC4D69EDFD10B584505BC24FBBBBC5604C03F21B63B0F9D +HASH TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.Race.cs = 7B372E1FED2F271E3A67CE14AA0CAF3A1B5E6E4C08A703E6F96C54C57776595C +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md index 3f706ae67..1d2f0472b 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md @@ -14,8 +14,99 @@ Output Summary: - Check: `Checked 3 files in 1021ms.` exit 0, no path listed. - Verdict: PASS. -## P1-T22 — HALTED PENDING MAINTAINER APPROVAL +## P1-T22 — HALTED PENDING MAINTAINER APPROVAL (SUPERSEDED by the "P1-T22 — RESULTS" section below) Timestamp: 2026-10-03T07-51 Task: P1-T22 Status: HALTED PENDING APPROVAL. No P1-T22 command (CMD-STRIPPED-COUNT, CMD-PHRASE-COUNT, CMD-PROTECTED-SPANS) has been run. Per the coordinator's binding instruction, execution stopped before P1-T22 so that a separate maintainer approval can be obtained (the CMD-PHRASE-COUNT payload with PHRASES-DOC was blocked at P0-T4 by `.claude/hooks/enforce-promotion-mcp-only.ps1`, and the 2026-10-03 one-time bypass covered P0-T4 only). The task remains unchecked in the plan; its census results will be appended to this file when it is executed. + +## P1-T22 — RESULTS + +Timestamp: 2026-10-03T08-05 +Task: P1-T22 +Command: CMD-STRIPPED-COUNT (TOKENS-STRUCT); CMD-PHRASE-COUNT (PHRASES-DOC); CMD-PROTECTED-SPANS (SIGNATURES-PROTECTED) over TaskMaster/Ribbon/EngineToggleStateCoordinator.cs, TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs and TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs +EXIT_CODE: 0 + +Output Summary: +- STRIPPED: 16 of 16 values equal their TOKENS-STRUCT required final values. +- PHRASE: 24 of 24 values equal their PHRASES-DOC required final values (coordinator-run rows, see below). +- SPAN-HASH: 14 of 14 lines read `equal=True` (twelve signatures and two field declarations). +- CATCH-SITES: two `catch` clauses, one in `HandleToggleClickAsync` and one in `TryInvokeSink`. +- Verdict: PASS (no EDIT SCOPE MISMATCH). + +### Precondition for the coordinator-run phrase census + +- `git rev-parse HEAD` printed `ca215e0683cbd3f10995272e1188e590a4405201`. +- `git diff --exit-code ca215e0683cbd3f10995272e1188e590a4405201 -- TaskMaster` printed nothing and exited 0. + +### STRIPPED (CMD-STRIPPED-COUNT, executor-run; observed value, required final value) + +- STRIPPED [catch(] = 2 (required 2) MATCH +- STRIPPED [catch(Exceptionex)] = 2 (required 2) MATCH +- STRIPPED [catch(Exception)] = 0 (required 0) MATCH +- STRIPPED [TryInvokeSink(] = 5 (required 5) MATCH +- STRIPPED [_logError(] = 3 (required 3) MATCH +- STRIPPED [_notifyUnavailable(] = 1 (required 1) MATCH +- STRIPPED [TryInvokeSink(()=>_notifyUnavailable(BuildUnavailableMessage(engineName)),outvarnotifyFailure)] = 1 (required 1) MATCH +- STRIPPED [TryInvokeSink(()=>_logError(BuildNotifyFailedMessage(engineName),notifyFailure),out_)] = 1 (required 1) MATCH +- STRIPPED [TryInvokeSink(()=>_logError(BuildToggleFailedMessage(engineName),ex),out_)] = 1 (required 1) MATCH +- STRIPPED [failure),out_)){_reportedPrimeFaults[reportKey]=0;}] = 1 (required 1) MATCH +- STRIPPED [_reportedPrimeFaults[reportKey]=0;] = 1 (required 1) MATCH +- STRIPPED [privatestaticboolTryInvokeSink(ActionsinkCall,outExceptionsinkFailure)] = 1 (required 1) MATCH +- STRIPPED [privatestaticstringBuildNotifyFailedMessage(stringengineName)] = 1 (required 1) MATCH +- STRIPPED [internalsealedclassEngineToggleStateCoordinator] = 0 (required 0) MATCH +- STRIPPED [internalsealedpartialclassEngineToggleStateCoordinator] = 3 (required 3) MATCH +- STRIPPED [_primeTasks.TryRemove(engineName,out_);] = 1 (required 1) MATCH + +### PHRASE (CMD-PHRASE-COUNT) + +Provenance: coordinator-run under the maintainer's second one-time bypass of enforce-promotion-mcp-only.ps1 (2026-10-03), worktree agent-a3fb26aa2afc7c52c, HEAD ca215e068, PowerShell tool. The executor did not run this command; the rows below are recorded verbatim as supplied, followed by the executor's comparison against the PHRASES-DOC required final value. + +- PHRASE [The in-flight] = 0 (required 0) MATCH +- PHRASE [most recently completed] = 0 (required 0) MATCH +- PHRASE [The prime task, or] = 0 (required 0) MATCH +- PHRASE [The registration marker for an engine key] = 1 (required 1) MATCH +- PHRASE [The marker is not the prime task itself] = 1 (required 1) MATCH +- PHRASE [The registered marker, or] = 1 (required 1) MATCH +- PHRASE [The other two are sink guards] = 0 (required 0) MATCH +- PHRASE [which holds the only other catch clause] = 1 (required 1) MATCH +- PHRASE [the only catch clause in this type that observes an engine fault] = 1 (required 1) MATCH +- PHRASE [The three] = 0 (required 0) MATCH +- PHRASE [all sit in] = 0 (required 0) MATCH +- PHRASE [The two catch clauses in this type are the click boundary] = 1 (required 1) MATCH +- PHRASE [also contains a failure of the sink] = 0 (required 0) MATCH +- PHRASE [routes its sink call through] = 1 (required 1) MATCH +- PHRASE [a sink failure is contained here] = 0 (required 0) MATCH +- PHRASE [a sink failure is contained by] = 1 (required 1) MATCH +- PHRASE [by the statement directly after the sink call] = 0 (required 0) MATCH +- PHRASE [by the only statement of the branch taken when] = 1 (required 1) MATCH +- PHRASE [never throws, even when the sink throws] = 0 (required 0) MATCH +- PHRASE [never throws on either path, even when both sinks throw] = 1 (required 1) MATCH +- PHRASE [honours its non-throwing precondition] = 2 (required 2) MATCH +- PHRASE [and must not throw] = 1 (required 1) MATCH +- PHRASE [The call is guarded (issue #964)] = 1 (required 1) MATCH +- PHRASE [Receives an observed prime fault, toggle fault or notification failure] = 1 (required 1) MATCH + +### SPAN-HASH (CMD-PROTECTED-SPANS, executor-run) + +- SPAN-HASH [internal EngineToggleStateCoordinator(] equal=True +- SPAN-HASH [internal bool GetPressed(string engineName)] equal=True +- SPAN-HASH [internal async Task ExecuteToggleAsync(string engineName)] equal=True +- SPAN-HASH [internal Task GetPrimeTask(string engineName)] equal=True +- SPAN-HASH [private void StartPrimeIfNeeded(string engineName, string controlId)] equal=True +- SPAN-HASH [private void StartObservedPrime(] equal=True +- SPAN-HASH [private async Task ApplyPrimeAsync(] equal=True +- SPAN-HASH [private static string RenderEngineName(string engineName)] equal=True +- SPAN-HASH [private static string BuildUnavailableMessage(string engineName)] equal=True +- SPAN-HASH [private static string BuildToggleFailedMessage(string engineName)] equal=True +- SPAN-HASH [private static string BuildPrimeFailedMessage(string engineName)] equal=True +- SPAN-HASH [private static string BuildUnmappedKeyMessage(string engineName)] equal=True +- SPAN-HASH [_primeTasks = new ConcurrentDictionary<] equal=True +- SPAN-HASH [(string EngineName, Type FaultType),] equal=True + +### CATCH-SITES (Read tool over the formatted TaskMaster/Ribbon/EngineToggleStateCoordinator.cs) + +CATCH-SITES: +- `HandleToggleClickAsync`: `try` at line 204, `catch (Exception ex)` clause lines 208 to 211 (body routes `_logError(BuildToggleFailedMessage(engineName), ex)` through `TryInvokeSink`). +- `TryInvokeSink`: `try` at line 289, `catch (Exception ex)` clause lines 295 to 299 (assigns `sinkFailure` and returns `false`). +- No `catch` clause in TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs or TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs (only documentation mentions of `catch`). diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/test-partials-unchanged.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/test-partials-unchanged.md new file mode 100644 index 000000000..9cf7d93b5 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/test-partials-unchanged.md @@ -0,0 +1,29 @@ +# Test Partials Unchanged (P1-T25, AC4) + +Timestamp: 2026-10-03T08-07 +Task: P1-T25 +Command: CMD-TEST-DIFF: git diff -U0 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs and -- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs; git diff --quiet 94287369908cc920b21b0e3256314f988ad7d2f5 -- +EXIT_CODE: 0 + +Output Summary: +- UNCHANGED exit=0 for PrimeFaultOrdering, PrimeRegistration, ThrowingSink and RepeatFaultSuppression (byte-equal to BASE-SHA). +- EngineToggleStateCoordinatorTests.Race.cs: minus=4 plus=4, NON-DOC-CHANGES = 0 (remark-only rewording, D-7a). +- EngineToggleStateCoordinatorTests.cs: minus=1 plus=12; the single MINUS line reads `message => Notifications.Add(message),` (the notify sink line replaced by the `OnNotify` harness member, delivered source T1). +- Verdict: PASS (no EXISTING TEST CHANGED). + +CMD-TEST-DIFF output: +``` +DIFF TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs minus=1 plus=12 +MINUS TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs :: message => Notifications.Add(message), +NON-DOC-CHANGES TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs = 8 +DIFF TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs minus=4 plus=4 +MINUS TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs :: /// canceled task and logs a second error. An error-count assertion taken after the re-prime +MINUS TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs :: /// would therefore be unsatisfiable by construction. The single-error assertion is made +MINUS TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs :: /// first, and the marker-cleared conclusion is drawn from prime-handle identity, which is +MINUS TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs :: /// deterministic. +NON-DOC-CHANGES TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs = 0 +UNCHANGED TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.PrimeFaultOrdering.cs exit=0 +UNCHANGED TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.PrimeRegistration.cs exit=0 +UNCHANGED TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.ThrowingSink.cs exit=0 +UNCHANGED TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.RepeatFaultSuppression.cs exit=0 +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-pass-after.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-pass-after.md new file mode 100644 index 000000000..0591557d5 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-pass-after.md @@ -0,0 +1,53 @@ +# Refusal Path Pass-After (P1-T23, P1-T24) + +## BUILD (P1-T23) + +Timestamp: 2026-10-03T08-06 +Task: P1-T23 +Command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" +EXIT_CODE: 0 + +Output Summary: +- MSBUILD_EXIT_CODE: 0 +- ERRORS: 0 +- TEST_DLL_ADVANCED: True +- CSC_OUT_TASKMASTER: 2 +- CSC_OUT_TASKMASTER_TEST: 2 +- Verdict: PASS (`MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `CSC_OUT_TASKMASTER:` at least 1, `TEST_DLL_ADVANCED: True`). The msbuild log stays under the git-ignored coverage/logs directory. + +## TEST RUN (P1-T24) + +Timestamp: 2026-10-03T08-07 +Task: P1-T24 +Command: vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\p1-t24" "/Logger:trx;LogFileName=p1-t24.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +- Pre-run check: STRAY_TEST_PROCESSES: 0. +- VSTEST_EXIT_CODE: 0; TRX_PRESENT: True; SEQUENCE_FILES: 0. +- COUNTERS total=43 executed=43 passed=43 failed=0 (BASELINE-TOTAL 39 plus 4). +- All 4 NEW-NAMES-964 and all 11 INVARIANT-NAMES entries = Passed; no FAILED line. +- Verdict: PASS (no PASS-AFTER NOT MET). The three FAIL-BEFORE-NAMES that failed at P1-T14 now pass against the fixed coordinator. + +CMD-VSTEST output: +``` +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=43 executed=43 passed=43 failed=0 +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain = Passed +RESULT GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared = Passed +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow = Passed +RESULT GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime = Passed +RESULT HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow = Passed +RESULT HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport = Passed +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime = Passed +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing = Passed +RESULT GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly = Passed +RESULT GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly = Passed +RESULT GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime = Passed +RESULT GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged = Passed +RESULT GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns = Passed +RESULT HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing = Passed +RESULT GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime = Passed +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 9b336a03c..9dd8ecf9b 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -909,15 +909,15 @@ Phase 1 is the constrained small-path implementation, executed task by task by t - Acceptance: the Grep tool counts `catch \(Exception\)` 0, `sink failure is contained here` 0 and `_reportedPrimeFaults\[reportKey\] = 0;` 1 in the file. - [x] [P1-T21] Insert delivered source F8 (`BuildNotifyFailedMessage`) into `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` with the Edit tool, directly after the closing brace of `BuildToggleFailedMessage`, then format the three coordinator files under TaskMaster/Ribbon with the P1-T6 format command and verify them with the P1-T6 check command (both wrapped per the Toolchain commands convention). - Acceptance: the Grep tool counts `private static string BuildNotifyFailedMessage\(string engineName\)` 1 in the Messages file; the check run exits 0 and prints `Checked 3 files` with no path listed (the success-case observation of the write-mode format). Both outputs and the Grep count are recorded under `FORMAT:` in FEATURE/evidence/qa-gates/production-edit-scope.md (this task creates that file, with `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`). -- [ ] [P1-T22] Verify the edit scope of the three files under TaskMaster/Ribbon (AC3, AC5, AC7) with `CMD-STRIPPED-COUNT` (`TOKENS-STRUCT`), `CMD-PHRASE-COUNT` (`PHRASES-DOC`) and `CMD-PROTECTED-SPANS` (`SIGNATURES-PROTECTED`), and append to FEATURE/evidence/qa-gates/production-edit-scope.md. +- [x] [P1-T22] Verify the edit scope of the three files under TaskMaster/Ribbon (AC3, AC5, AC7) with `CMD-STRIPPED-COUNT` (`TOKENS-STRUCT`), `CMD-PHRASE-COUNT` (`PHRASES-DOC`) and `CMD-PROTECTED-SPANS` (`SIGNATURES-PROTECTED`), and append to FEATURE/evidence/qa-gates/production-edit-scope.md. - Acceptance, all required: every `STRIPPED` value equals its required final value in `TOKENS-STRUCT`; every `PHRASE` value equals its required final value in `PHRASES-DOC`; every `SPAN-HASH` line reads `equal=True` (twelve signatures and two field declarations). Together with the P0-T4 base values this shows each token and phrase moving from its false-before to its true-after value. The artifact also records, from reading the formatted files with the Read tool, the line ranges of the two remaining `catch` clauses (one in `HandleToggleClickAsync`, one in `TryInvokeSink`) as `CATCH-SITES:`. Any mismatch is `EDIT SCOPE MISMATCH`: correct the edit against the delivered source with the Edit tool and re-run P1-T21 and this task; after two failed corrections stop. -- [ ] [P1-T23] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t23) and record the build lines at the top of FEATURE/evidence/regression-testing/refusal-path-pass-after.md (this task creates the file). +- [x] [P1-T23] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t23) and record the build lines at the top of FEATURE/evidence/regression-testing/refusal-path-pass-after.md (this task creates the file). - Acceptance: `MSBUILD_EXIT_CODE: 0`, `ERRORS: 0`, `CSC_OUT_TASKMASTER:` at least 1 and `TEST_DLL_ADVANCED: True`. -- [ ] [P1-T24] Run the fixture against the fixed coordinator with `CMD-VSTEST` over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll (`TASKID` p1-t24, `NAMES` `NAMES-ALL`) and append the result to FEATURE/evidence/regression-testing/refusal-path-pass-after.md. +- [x] [P1-T24] Run the fixture against the fixed coordinator with `CMD-VSTEST` over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll (`TASKID` p1-t24, `NAMES` `NAMES-ALL`) and append the result to FEATURE/evidence/regression-testing/refusal-path-pass-after.md. - Acceptance, all required: `VSTEST_EXIT_CODE: 0`; `SEQUENCE_FILES: 0`; `COUNTERS` total equal to `BASELINE-TOTAL` plus 4 (43), `passed` equal to `total`, `failed=0`; every `NEW-NAMES-964` and every `INVARIANT-NAMES` entry has a `RESULT ... = Passed` line; no `FAILED` line. Anything else is `PASS-AFTER NOT MET`: stop and report. -- [ ] [P1-T25] Verify that no existing test assertion in TaskMaster.Test/Ribbon was weakened or removed (AC4) with `CMD-TEST-DIFF`, and record FEATURE/evidence/qa-gates/test-partials-unchanged.md. +- [x] [P1-T25] Verify that no existing test assertion in TaskMaster.Test/Ribbon was weakened or removed (AC4) with `CMD-TEST-DIFF`, and record FEATURE/evidence/qa-gates/test-partials-unchanged.md. - Acceptance, all required: the four `UNCHANGED` lines read `exit=0` (PrimeFaultOrdering, PrimeRegistration, ThrowingSink and RepeatFaultSuppression byte-equal to BASE-SHA); for `EngineToggleStateCoordinatorTests.Race.cs` `NON-DOC-CHANGES` is 0; for `EngineToggleStateCoordinatorTests.cs` the `DIFF` line shows `minus=1` and the single `MINUS` line reads `message => Notifications.Add(message),`. Any other result is `EXISTING TEST CHANGED`: stop. -- [ ] [P1-T26] Measure every coordinator source file under TaskMaster/Ribbon and every fixture partial under TaskMaster.Test/Ribbon (AC6) with `CMD-LINECOUNT` and `CMD-HASH`, check the csproj registrations, and record FEATURE/evidence/qa-gates/file-line-counts.md. +- [x] [P1-T26] Measure every coordinator source file under TaskMaster/Ribbon and every fixture partial under TaskMaster.Test/Ribbon (AC6) with `CMD-LINECOUNT` and `CMD-HASH`, check the csproj registrations, and record FEATURE/evidence/qa-gates/file-line-counts.md. - Command: `CMD-LINECOUNT`; `CMD-HASH`; the Grep tool counts of `Ribbon\x5CEngineToggleStateCoordinator\.` over `TaskMaster/TaskMaster.csproj` and of `Ribbon\x5CEngineToggleStateCoordinatorTests` over `TaskMaster.Test/TaskMaster.Test.csproj`. - Acceptance, all required: `PRODUCTION-FILES: 3` and every production `LINES` value at most 450 (expected about 307, 196 and 86 for the main, Prime and Messages files; observations only); `TEST-PARTIALS: 7` and every test `LINES` value at most 500 (primary fixture expected 481, Race 277, SinkGuard about 175); the production csproj has 3 coordinator compile entries and the test csproj 7 fixture entries, each file named by a `LINES` row registered exactly once. The `HASH` values are recorded as `PHASE1-HASHES:` for P2-T1. Any production file over 450 or test file over 500 is `FILE SIZE CEILING EXCEEDED`: stop. From f179a4426c32d2a8305f7c8ca1286b9180b3b76b Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 08:16:08 -0400 Subject: [PATCH 09/17] docs(964): record P2-T1 to P2-T8 final toolchain pass, coverage comparison and footprint evidence Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../evidence/qa-gates/coverage-comparison.md | 31 ++++ .../evidence/qa-gates/coverage-final.md | 166 +++++++++++++++++ .../qa-gates/csharpier-check-final.md | 12 ++ .../evidence/qa-gates/csharpier-format.md | 32 ++++ .../evidence/qa-gates/footprint-scope.md | 174 ++++++++++++++++++ .../qa-gates/msbuild-analyzer-final.md | 16 ++ .../qa-gates/msbuild-nullable-final.md | 16 ++ .../evidence/qa-gates/toolchain-final-pass.md | 15 ++ .../plan.2026-10-02T05-20.md | 16 +- 9 files changed, 470 insertions(+), 8 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-comparison.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-final.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-check-final.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-format.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/footprint-scope.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-analyzer-final.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-nullable-final.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/toolchain-final-pass.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-comparison.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-comparison.md new file mode 100644 index 000000000..22d531152 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-comparison.md @@ -0,0 +1,31 @@ +# Coverage Comparison (P2-T6, AC8) + +Timestamp: 2026-10-03T08-13 +Task: P2-T6 +Command: Read FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-final.md (Read tool) and compare the recorded figures +EXIT_CODE: 0 + +Output Summary: +- BASELINE-FIRST-PARTY: lines 56609/65855 (85.96%), branches 13680/17078 (80.10%) +- FINAL-FIRST-PARTY: lines 56629/65881 (85.96%), branches 13683/17082 (80.10%) +- BASELINE-COORD-LINES: covered=177 valid=177 (one file) +- FINAL-COORD-LINES: covered=203 valid=203 (three files: main 89/89, Prime 72/72, Messages 42/42) +- BASELINE-COORD-LINE-RATE: 100 +- FINAL-COORD-LINE-RATE: 100 +- METHOD HandleToggleClickAsync file=TaskMaster/Ribbon/EngineToggleStateCoordinator.cs span=184-212 nodes=1 elements=24 covered=24 uncovered=0 rate=100 +- METHOD CompletePrime file=TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs span=165-195 nodes=1 elements=22 covered=22 uncovered=0 rate=100 +- METHOD TryInvokeSink file=TaskMaster/Ribbon/EngineToggleStateCoordinator.cs span=287-300 nodes=1 elements=10 covered=10 uncovered=0 rate=100 +- METHOD BuildNotifyFailedMessage file=TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs span=50-58 nodes=1 elements=8 covered=8 uncovered=0 rate=100 +- NEW-CODE-COVERAGE: TryInvokeSink 100; BuildNotifyFailedMessage 100 +- Verdict: PASS (every clause MET; no COORDINATOR COVERAGE LOWERED). + +Clauses: +- FINAL-COORD-LINE-RATE (100) at least BASELINE-COORD-LINE-RATE (100): MET +- METHOD TryInvokeSink rate (100) at least 90.00: MET +- METHOD BuildNotifyFailedMessage rate (100) at least 90.00: MET +- METHOD HandleToggleClickAsync uncovered (0) at most BASELINE-METHOD-HTC-UNCOVERED (0): MET +- METHOD CompletePrime uncovered (0) at most BASELINE-METHOD-CP-UNCOVERED (0): MET +- Final line floor (85.96% against 80%): MET (LINE-FLOOR: MET in coverage-final.md) +- Final branch floor (80.10% against 75%): MET (BRANCH-FLOOR: MET in coverage-final.md) + +Observation (not a gate): the `UtilitiesCS` package line count moved from 39306 covered / 4202 missed at baseline to 39300 covered / 4208 missed at final, and its branch count from 9494 / 1799 to 9493 / 1800. This item changed no file under UtilitiesCS; the movement is run-to-run variance in code outside the Write Set. The `TaskMaster` package moved from 2477 to 2503 covered lines with 802 missed at both stages. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-final.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-final.md new file mode 100644 index 000000000..7eaec433a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-final.md @@ -0,0 +1,166 @@ +# Final: repository test-and-coverage run (P2-T5, pass 1) + +Timestamp: 2026-10-03T08-13 +Task: P2-T5 +Command: pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1 +EXIT_CODE: 0 + +Output Summary: +- RUNNER_EXIT_CODE: 0 (D-8 test-step rule: exit 0) +- RAW: False +- LINE-FLOOR: MET +- BRANCH-FLOOR: MET +- First-party coverage: lines 56629/65881 (85.96%), branches 13683/17082 (80.10%) +- ROOT line-rate=0.859565 branch-rate=0.801019 lines-covered=56629 lines-valid=65881 +- COORD-LINES covered=203 valid=203 +- COORD-LINE-RATE: 100 +- FINAL-FAILED-FQN-COUNT: 0 (no FAILED-FQN rows) +- Verdict: PASS (COORD-CLASS-NODES: 3; every COORD-FILE row nodes=1; no PARTIAL CLASS ATTRIBUTION UNSUPPORTED, no NEW FAILING TEST). + +Details: + +- Pre-run check: STRAY_TEST_PROCESSES: 0. +- DISCOVERED_LINE: Discovered 9 test assemblies. +- FIRST_PARTY_LINE: First-party coverage: lines 56629/65881 (85.96%), branches 13683/17082 (80.10%) +- THRESHOLD_MESSAGE: (empty) +- COLLECT_FAILURE_MESSAGE: (empty) +- DOCUMENT_PRESENT: True +- TRX_PRESENT: True +- SUMMARY_FILE_PRESENT: True +- FAILED-SET: (empty) +- TEST-DEFINITIONS: 7378 +- FAILED-FQN-COUNT: 0 (no FAILED-FQN rows) + +SUMMARY-BEGIN +``` +Test run outcome: Completed +Total 7388, executed 7388, passed 7388, failed 0. +Skipped 0, derived as total minus executed rather than reported by the test platform. +Figures reported verbatim by the test platform: error 0, timeout 0, aborted 0, notExecuted 0, inconclusive 0. +Failed tests: none +``` +SUMMARY-END + +PROJECTION-BEGIN +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` +PROJECTION-END + +Coordinator rows: +``` +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.cs nodes=1 covered=89 valid=89 +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs nodes=1 covered=72 valid=72 +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs nodes=1 covered=42 valid=42 +COORD-CLASS-NODES: 3 +COORD-LINES covered=203 valid=203 +COORD-BRANCHES covered=43 valid=44 +COORD-LINE-RATE: 100 +METHOD HandleToggleClickAsync file=TaskMaster/Ribbon/EngineToggleStateCoordinator.cs span=184-212 nodes=1 elements=24 covered=24 uncovered=0 rate=100 +METHOD-LINE HandleToggleClickAsync 185 hits=1 +METHOD-LINE HandleToggleClickAsync 186 hits=1 +METHOD-LINE HandleToggleClickAsync 187 hits=1 +METHOD-LINE HandleToggleClickAsync 188 hits=1 +METHOD-LINE HandleToggleClickAsync 189 hits=1 +METHOD-LINE HandleToggleClickAsync 190 hits=1 +METHOD-LINE HandleToggleClickAsync 191 hits=1 +METHOD-LINE HandleToggleClickAsync 192 hits=1 +METHOD-LINE HandleToggleClickAsync 193 hits=1 +METHOD-LINE HandleToggleClickAsync 194 hits=1 +METHOD-LINE HandleToggleClickAsync 195 hits=1 +METHOD-LINE HandleToggleClickAsync 196 hits=1 +METHOD-LINE HandleToggleClickAsync 197 hits=1 +METHOD-LINE HandleToggleClickAsync 198 hits=1 +METHOD-LINE HandleToggleClickAsync 199 hits=1 +METHOD-LINE HandleToggleClickAsync 201 hits=1 +METHOD-LINE HandleToggleClickAsync 205 hits=1 +METHOD-LINE HandleToggleClickAsync 206 hits=1 +METHOD-LINE HandleToggleClickAsync 207 hits=1 +METHOD-LINE HandleToggleClickAsync 208 hits=1 +METHOD-LINE HandleToggleClickAsync 209 hits=1 +METHOD-LINE HandleToggleClickAsync 210 hits=1 +METHOD-LINE HandleToggleClickAsync 211 hits=1 +METHOD-LINE HandleToggleClickAsync 212 hits=1 +METHOD CompletePrime file=TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs span=165-195 nodes=1 elements=22 covered=22 uncovered=0 rate=100 +METHOD-LINE CompletePrime 166 hits=1 +METHOD-LINE CompletePrime 167 hits=1 +METHOD-LINE CompletePrime 168 hits=1 +METHOD-LINE CompletePrime 169 hits=1 +METHOD-LINE CompletePrime 172 hits=1 +METHOD-LINE CompletePrime 173 hits=1 +METHOD-LINE CompletePrime 174 hits=1 +METHOD-LINE CompletePrime 180 hits=1 +METHOD-LINE CompletePrime 181 hits=1 +METHOD-LINE CompletePrime 182 hits=1 +METHOD-LINE CompletePrime 183 hits=1 +METHOD-LINE CompletePrime 184 hits=1 +METHOD-LINE CompletePrime 185 hits=1 +METHOD-LINE CompletePrime 186 hits=1 +METHOD-LINE CompletePrime 187 hits=1 +METHOD-LINE CompletePrime 188 hits=1 +METHOD-LINE CompletePrime 189 hits=1 +METHOD-LINE CompletePrime 190 hits=1 +METHOD-LINE CompletePrime 191 hits=1 +METHOD-LINE CompletePrime 192 hits=1 +METHOD-LINE CompletePrime 194 hits=1 +METHOD-LINE CompletePrime 195 hits=1 +METHOD TryInvokeSink file=TaskMaster/Ribbon/EngineToggleStateCoordinator.cs span=287-300 nodes=1 elements=10 covered=10 uncovered=0 rate=100 +METHOD-LINE TryInvokeSink 288 hits=1 +METHOD-LINE TryInvokeSink 290 hits=1 +METHOD-LINE TryInvokeSink 291 hits=1 +METHOD-LINE TryInvokeSink 292 hits=1 +METHOD-LINE TryInvokeSink 293 hits=1 +METHOD-LINE TryInvokeSink 295 hits=1 +METHOD-LINE TryInvokeSink 296 hits=1 +METHOD-LINE TryInvokeSink 297 hits=1 +METHOD-LINE TryInvokeSink 298 hits=1 +METHOD-LINE TryInvokeSink 300 hits=1 +METHOD BuildNotifyFailedMessage file=TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs span=50-58 nodes=1 elements=8 covered=8 uncovered=0 rate=100 +METHOD-LINE BuildNotifyFailedMessage 51 hits=1 +METHOD-LINE BuildNotifyFailedMessage 52 hits=1 +METHOD-LINE BuildNotifyFailedMessage 53 hits=1 +METHOD-LINE BuildNotifyFailedMessage 54 hits=1 +METHOD-LINE BuildNotifyFailedMessage 55 hits=1 +METHOD-LINE BuildNotifyFailedMessage 56 hits=1 +METHOD-LINE BuildNotifyFailedMessage 57 hits=1 +METHOD-LINE BuildNotifyFailedMessage 58 hits=1 +``` + +The raw documents `coverage\final-964.cobertura.xml` and `coverage\final-964.trx` stay on disk under the git-ignored coverage directory. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-check-final.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-check-final.md new file mode 100644 index 000000000..9a132a67d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-check-final.md @@ -0,0 +1,12 @@ +# CSharpier Check Final (P2-T2, pass 1) + +Timestamp: 2026-10-03T08-09 +Task: P2-T2 +Command: dotnet tool run csharpier check . (run from the worktree root) +EXIT_CODE: 0 + +Output Summary: +- CSHARPIER_EXIT_CODE: 0 +- `Checked 1640 files in 5276ms.` +- No path reported as unformatted. +- Verdict: PASS. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-format.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-format.md new file mode 100644 index 000000000..1070d961a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-format.md @@ -0,0 +1,32 @@ +# CSharpier Format (P2-T1, pass 1) + +Timestamp: 2026-10-03T08-09 +Task: P2-T1 +Command: dotnet tool run csharpier format . (run from the worktree root; preceded and followed by CMD-HASH and git status --porcelain --untracked-files=all) +EXIT_CODE: 0 + +Output Summary: +- CSHARPIER_EXIT_CODE: 0; `Formatted 1640 files in 5841ms.` (processed count, not an assertion). +- Before-format CMD-HASH values equal PHASE1-HASHES (FEATURE/evidence/qa-gates/file-line-counts.md) for all six Write Set C# files. +- After-format CMD-HASH values equal the before-format values for all six files: the formatter rewrote no Write Set file. +- Porcelain listings before and after the format are identical (four untracked `.claude/agent-memory/` entries, ambient). +- Pass number: 1; no restart required. +- Verdict: PASS. + +CMD-HASH (before and after, identical): +``` +HASH TaskMaster\Ribbon\EngineToggleStateCoordinator.cs = 8836AEFB84FA685EAD4EFB993C3A0E1492EB21606A06CF0B2FE1497E872E4EF4 +HASH TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs = 50DC74A866D6310184EA89C2773875994D9C73EEB3DF11B63244A80B80AC52CD +HASH TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs = 1E52B69D775038726117DEB0A63AEAC301A13E2FA5B9C96E6B00CC63046159AC +HASH TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs = 3E905F281D4493F289783EDE8BC9C98127547349CDDBB52E5D6DC3D692604211 +HASH TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs = D172DA1C8E2596198AC4D69EDFD10B584505BC24FBBBBC5604C03F21B63B0F9D +HASH TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.Race.cs = 7B372E1FED2F271E3A67CE14AA0CAF3A1B5E6E4C08A703E6F96C54C57776595C +``` + +Porcelain (before and after, identical): +``` +?? .claude/agent-memory/atomic-planner/project_964_partial_split_sink_guard_plan_seams.md +?? .claude/agent-memory/atomic-planner/project_964_r2_preparation_record_closed_evidence_set.md +?? .claude/agent-memory/atomic-planner/project_964_r3_glob_backslash_and_hit_attribution_seams.md +?? .claude/agent-memory/orchestrator/preparation-clearance-record-breaks-closed-evidence-set.md +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/footprint-scope.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/footprint-scope.md new file mode 100644 index 000000000..f92594151 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/footprint-scope.md @@ -0,0 +1,174 @@ +# Footprint Scope (P2-T8) + +Timestamp: 2026-10-03T08-14 +Task: P2-T8 +Command: git diff --name-only 981abef77657adcc90d7c116a6b4c6500b79ea29; git status --porcelain --untracked-files=all; negative controls git diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29 and git diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29 -- TaskMaster TaskMaster.Test +EXIT_CODE: 0 + +Output Summary: +- Diff listing against MERGE-SHA: 31 paths: the eight Write Set code paths, and 23 paths under the feature folder (evidence files and the plan). +- Porcelain listing: 12 entries: 1 modified plan file and 7 untracked evidence files under the feature folder, and 4 untracked `.claude/agent-memory/` files (ambient, never staged). +- Every path is a Write Set code path, lies under the feature folder, or lies under `.claude/agent-memory/`. No FOOTPRINT EXCEEDS WRITE SET. +- Positive control: the union of the two listings contains all eight Write Set code paths (all eight appear in the diff listing because orchestrator phase-boundary commits after MERGE-SHA recorded them; none appears in the porcelain listing). +- Negative control 1 (BASE-SHA to MERGE-SHA): non-empty (99 paths) and contains the line `.gitignore`, a path outside the Write Set. No FOOTPRINT CONTROL INERT. +- Negative control 2 (BASE-SHA to MERGE-SHA under TaskMaster and TaskMaster.Test): empty. No MERGE TOUCHED ITEM CODE. +- Verdict: PASS. + +Diff listing (git diff --name-only 981abef77657adcc90d7c116a6b4c6500b79ea29): +``` +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs +TaskMaster.Test/TaskMaster.Test.csproj +TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs +TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs +TaskMaster/Ribbon/EngineToggleStateCoordinator.cs +TaskMaster/TaskMaster.csproj +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-production.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/anchor-test-side.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-dotnet-coverage.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-nuget-restore.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-sdk.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/bootstrap-tool-restore.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coordinator-tests-baseline.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/csharpier-check-baseline.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-analyzer-baseline.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/msbuild-nullable-baseline.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/phase0-instructions-read.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/scope-and-anchor.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/implementation-handoff.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/file-line-counts.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/production-edit-scope.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/test-partials-unchanged.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-fail-before.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/refusal-path-pass-after.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/sink-guard-partial-tokens.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-census.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/split-fixture-green.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +``` + +Porcelain listing (git status --porcelain --untracked-files=all): +``` + M docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +?? .claude/agent-memory/atomic-planner/project_964_partial_split_sink_guard_plan_seams.md +?? .claude/agent-memory/atomic-planner/project_964_r2_preparation_record_closed_evidence_set.md +?? .claude/agent-memory/atomic-planner/project_964_r3_glob_backslash_and_hit_attribution_seams.md +?? .claude/agent-memory/orchestrator/preparation-clearance-record-breaks-closed-evidence-set.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-comparison.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-final.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-check-final.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/csharpier-format.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-analyzer-final.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-nullable-final.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/toolchain-final-pass.md +``` + +Negative control 1 (git diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29): +``` +.claude/agent-memory/orchestrator/MEMORY.md +.claude/agent-memory/orchestrator/poshqc-gates-observed-outputs-for-scripts-hygiene.md +.github/workflows/README.md +.gitignore +TaskMaster.sln.bak +TaskTree/TaskTree.vbproj.bak +TaskVisualization/TaskVisualization.vbproj.bak +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md +docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T05-30.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T06-18.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t10-ac2-baseline.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t11-check-ignore-control.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t12-guard-baseline.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t13-format-baseline.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t14-analyze-baseline.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t15-test-baseline.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t16-coverage-limitation.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t2-feature-folder-preconditions.2026-10-02T05-08.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t3-base-sha.2026-10-02T05-08.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t4-carried-docs.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t5-ac1-baseline.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t6-icase-inventory.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t7-worktree-inventory.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t8-no-reader-search.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t9-search-control.2026-10-02T05-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/phase0-instructions-read.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/ci-pester-coverage.2026-10-02T09-45.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t1-implementation-handoff.2026-10-02T05-12.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t10-git-rm.2026-10-02T05-16.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t11-gitignore-edit.2026-10-02T05-16.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t12-readme-row-edit.2026-10-02T05-16.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t2-rule-tests-added.2026-10-02T05-13.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t3-orchestration-tests-added.2026-10-02T05-13.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t5-rule-function-added.2026-10-02T05-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t6-guard-rule-wired.2026-10-02T05-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t7-guard-docs-updated.2026-10-02T05-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p2-t25-audit-handoff.2026-10-02T05-20.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t1-remediation-handoff.2026-10-02T06-07.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t13-gitignore-redundant-lines-removed.2026-10-02T06-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t18-citation-scan.2026-10-02T06-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t27-rereview-handoff.2026-10-02T06-15.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t5-cr1-lookalike-split.2026-10-02T06-09.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t6-cr2-backup-test-because.2026-10-02T06-09.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p3-t7-cr2-sibling-because.2026-10-02T06-09.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/preflight-clearance.2026-10-02T03-30.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t1-format.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t10-ac2-negative-control.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t11-ac2-exact-line.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t12-ac7-readme-row.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t13-stage-footprint.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t14-base-continuity.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t15-footprint-non-docs.2026-10-02T05-18.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t16-footprint-feature-folder.2026-10-02T05-18.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t17-guard-final.2026-10-02T05-18.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t2-analyze.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t3-test.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t4-statement-coverage-map.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t5-file-sizes.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t6-ac1-index.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t7-ac1-worktree-absence.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t8-ac2-check-ignore-q.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t9-ac2-check-ignore-v.2026-10-02T05-17.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t14-ac2-samples-after-removal.2026-10-02T06-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t15-ac2-check-ignore-q.2026-10-02T06-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t16-ac2-negative-control.2026-10-02T06-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t17-ac2-exact-line.2026-10-02T06-11.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t19-format.2026-10-02T06-12.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t20-analyze.2026-10-02T06-12.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t21-test.2026-10-02T06-12.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t22-stage-footprint.2026-10-02T06-13.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t23-guard-final.2026-10-02T06-13.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t24-footprint-non-docs.2026-10-02T06-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t25-footprint-feature-folder.2026-10-02T06-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t26-base-continuity.2026-10-02T06-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p3-t8-cr2-rules-tests-because-sweep.2026-10-02T06-09.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p1-t4-expect-fail-test-run.2026-10-02T05-13.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p1-t8-test-run-pass.2026-10-02T05-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p1-t9-guard-negative-control.2026-10-02T05-14.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t10-control-plain-bak-unignored.2026-10-02T06-10.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t11-control-specific-lines-only.2026-10-02T06-10.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t12-control-restored.2026-10-02T06-10.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t2-pre-remediation-fingerprints.2026-10-02T06-08.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t3-pre-remediation-test-run.2026-10-02T06-08.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t4-pre-removal-check-ignore.2026-10-02T06-08.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p3-t9-control-covering-rule-removed.2026-10-02T06-10.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T05-30.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T06-18.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/plan.2026-10-02T02-25.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T05-30.md +docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T06-18.md +docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md +docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md +scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 +scripts/hygiene/Test-RepositoryHygiene.ps1 +tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 +tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 +``` + +Negative control 2 (git diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29 -- TaskMaster TaskMaster.Test): +``` +(empty) +``` diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-analyzer-final.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-analyzer-final.md new file mode 100644 index 000000000..4d489a276 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-analyzer-final.md @@ -0,0 +1,16 @@ +# MSBuild Analyzer Final (P2-T3, pass 1) + +Timestamp: 2026-10-03T08-10 +Task: P2-T3 +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true +EXIT_CODE: 0 + +Output Summary: +- MSBUILD_EXIT_CODE: 0 +- ERRORS: 0 +- WARNINGS: 0 (ANALYZER-BASELINE-WARNINGS: 0; observation) +- CSC_OUT_TASKMASTER: 2 +- CSC_OUT_TASKMASTER_TEST: 2 +- WRITESET_DIAGNOSTIC_LINES: 0 +- TEST_DLL_EXISTS: True +- Verdict: PASS. The msbuild log (coverage/logs/p2-t3.msbuild.log) stays under the git-ignored coverage directory. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-nullable-final.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-nullable-final.md new file mode 100644 index 000000000..8c5d92a9d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/msbuild-nullable-final.md @@ -0,0 +1,16 @@ +# MSBuild Nullable Final (P2-T4, pass 1) + +Timestamp: 2026-10-03T08-10 +Task: P2-T4 +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true +EXIT_CODE: 0 + +Output Summary: +- MSBUILD_EXIT_CODE: 0 +- ERRORS: 0 +- WARNINGS: 0 (NULLABLE-BASELINE-WARNINGS: 0; observation) +- CSC_OUT_TASKMASTER: 2 +- CSC_OUT_TASKMASTER_TEST: 2 +- WRITESET_DIAGNOSTIC_LINES: 0 +- TEST_DLL_EXISTS: True +- Verdict: PASS. The msbuild log (coverage/logs/p2-t4.msbuild.log) stays under the git-ignored coverage directory. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/toolchain-final-pass.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/toolchain-final-pass.md new file mode 100644 index 000000000..72a857afe --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/toolchain-final-pass.md @@ -0,0 +1,15 @@ +# Toolchain Final Pass (P2-T7) + +Timestamp: 2026-10-03T08-13 +Task: P2-T7 +Command: dotnet tool run csharpier format . with dotnet tool run csharpier check .; msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true; msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true; pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1 +EXIT_CODE: 0 + +Output Summary: +- Pass number: 1 (P2-T1 rewrote no Write Set file and the porcelain listing was unchanged, so no restart was required). +- Step 1 format: FEATURE/evidence/qa-gates/csharpier-format.md — exit 0, `Formatted 1640 files`, Write Set hashes unchanged. PASS. FEATURE/evidence/qa-gates/csharpier-check-final.md — exit 0, `Checked 1640 files`, no unformatted path. PASS. +- Step 2 analyzers: FEATURE/evidence/qa-gates/msbuild-analyzer-final.md — exit 0, ERRORS 0, WARNINGS 0, CSC_OUT 2/2, WRITESET_DIAGNOSTIC_LINES 0. PASS. +- Step 3 type-check: FEATURE/evidence/qa-gates/msbuild-nullable-final.md — exit 0, ERRORS 0, WARNINGS 0, CSC_OUT 2/2, WRITESET_DIAGNOSTIC_LINES 0. PASS. +- Step 4 test and coverage: FEATURE/evidence/qa-gates/coverage-final.md — runner exit 0, 7388 of 7388 passed, LINE-FLOOR MET (85.96%), BRANCH-FLOOR MET (80.10%), COORD-LINE-RATE 100. PASS. +- D-8 test-step outcome: runner exit 0 (no failing test; FAILED-FQN-COUNT 0). +- Verdict: one clean pass of all four steps. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 9dd8ecf9b..1d78a77ef 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -925,23 +925,23 @@ Phase 1 is the constrained small-path implementation, executed task by task by t No code file is edited in Phase 2. If P2-T1 rewrites a Write Set file, the loop restarts once from P2-T1 (recorded as `PASS-2:` sections in the same artifacts); any other failure of P2-T1 to P2-T5 stops the plan with its artifact, and the fix returns to the orchestrator as a remediation round. -- [ ] [P2-T1] Apply repository-wide formatting from the worktree root (TaskMaster.sln tree, `.csharpierignore` applies) with `dotnet tool run csharpier format .` and record FEATURE/evidence/qa-gates/csharpier-format.md. +- [x] [P2-T1] Apply repository-wide formatting from the worktree root (TaskMaster.sln tree, `.csharpierignore` applies) with `dotnet tool run csharpier format .` and record FEATURE/evidence/qa-gates/csharpier-format.md. - Command: `CMD-HASH` and `git -C WORKTREE status --porcelain --untracked-files=all` before the format; `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier format .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH` and the same porcelain command again. - Acceptance, all required: `EXIT_CODE: 0`; the `Formatted N files` line is recorded as a processed count, not an assertion; the before-and-after tree observation holds: every `HASH` value after the format equals the value before it (in pass 1 the before values must also equal `PHASE1-HASHES:`) and the two porcelain listings are identical. In pass 1 a differing Write Set hash restarts the loop once from this task as stated above, and pass 2 compares against the hashes recorded after the pass-1 format; a differing hash in pass 2 is `FORMAT NOT STABLE`: stop. A changed porcelain entry outside the Write Set is `FORMAT TOUCHED OUT-OF-SCOPE FILE`: stop. -- [ ] [P2-T2] Verify formatting read-only from the worktree root (TaskMaster.sln tree) with `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/csharpier-check-final.md. +- [x] [P2-T2] Verify formatting read-only from the worktree root (TaskMaster.sln tree) with `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/csharpier-check-final.md. - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'` - Acceptance: `EXIT_CODE: 0`, the `Checked N files` line is recorded, and no path is reported as unformatted. -- [ ] [P2-T3] Run the analyzer gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p2-t3) and record FEATURE/evidence/qa-gates/msbuild-analyzer-final.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). +- [x] [P2-T3] Run the analyzer gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p2-t3) and record FEATURE/evidence/qa-gates/msbuild-analyzer-final.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded beside `ANALYZER-BASELINE-WARNINGS:` as an observation. -- [ ] [P2-T4] Run the type-check gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p2-t4) and record FEATURE/evidence/qa-gates/msbuild-nullable-final.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). +- [x] [P2-T4] Run the type-check gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p2-t4) and record FEATURE/evidence/qa-gates/msbuild-nullable-final.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded beside `NULLABLE-BASELINE-WARNINGS:` as an observation. -- [ ] [P2-T5] Run the test-and-coverage gate with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` (`STAGE` final) and then `CMD-COVERAGE-POST` (`STAGE` final, `RAW` per its rule), and record FEATURE/evidence/qa-gates/coverage-final.md with the same artifact layout as P0-T14 (numeric post-change figures in `Output Summary:`: the `First-party coverage:` line, the `ROOT` line, `COORD-LINES` and `COORD-LINE-RATE:`; `FINAL-FAILED-FQN-COUNT:` and every `FAILED-FQN` row). +- [x] [P2-T5] Run the test-and-coverage gate with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` (`STAGE` final) and then `CMD-COVERAGE-POST` (`STAGE` final, `RAW` per its rule), and record FEATURE/evidence/qa-gates/coverage-final.md with the same artifact layout as P0-T14 (numeric post-change figures in `Output Summary:`: the `First-party coverage:` line, the `ROOT` line, `COORD-LINES` and `COORD-LINE-RATE:`; `FINAL-FAILED-FQN-COUNT:` and every `FAILED-FQN` row). - Acceptance, all required: `DOCUMENT_PRESENT: True` and `TRX_PRESENT: True`; `LINE-FLOOR: MET`, `BRANCH-FLOOR: MET` and an empty `THRESHOLD_MESSAGE:`; the test-step rule of D-8 holds: `EXIT_CODE: 0`, or a non-zero exit with a non-empty `COLLECT_FAILURE_MESSAGE:`, `TEST-DEFINITIONS:` at least 1, `FAILED-FQN-COUNT:` at least 1, no `FAILED-FQN` value beginning `UNRESOLVED:` or `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.`, and every `FAILED-FQN` value present verbatim among the `FAILED-FQN` rows of `FEATURE/evidence/baseline/coverage-baseline.md` (then `ExpectedExitCode:` equals the observed value and the artifact records `TEST-STEP: PASS (PRE-EXISTING FAILURES ONLY)`); every `COORD-FILE` row reads `nodes=` at least 1 (otherwise `PARTIAL CLASS ATTRIBUTION UNSUPPORTED`: stop); the summary first line begins `Test run outcome:`; the projection contains the `TaskMaster` package; no absolute path in the artifact. A `FAILED-FQN` value absent from the baseline rows is `NEW FAILING TEST`: stop without a re-run. `coverage\final-964.cobertura.xml` and `coverage\final-964.trx` stay on disk, git-ignored. -- [ ] [P2-T6] Compare baseline and post-change coverage for the coordinator files under TaskMaster/Ribbon and record FEATURE/evidence/qa-gates/coverage-comparison.md (sources: FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-final.md). +- [x] [P2-T6] Compare baseline and post-change coverage for the coordinator files under TaskMaster/Ribbon and record FEATURE/evidence/qa-gates/coverage-comparison.md (sources: FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-final.md). - Acceptance, all required: the artifact carries `Timestamp:`, `Command:` (the two source artifacts read), `EXIT_CODE: 0` and an `Output Summary:` with `BASELINE-FIRST-PARTY:` and `FINAL-FIRST-PARTY:` (repository line and branch percentages), `BASELINE-COORD-LINES:`, `FINAL-COORD-LINES:`, `BASELINE-COORD-LINE-RATE:`, `FINAL-COORD-LINE-RATE:`, the four final `METHOD` rows and `NEW-CODE-COVERAGE:` (the `TryInvokeSink` and `BuildNotifyFailedMessage` rates). Clauses: `FINAL-COORD-LINE-RATE` at least `BASELINE-COORD-LINE-RATE` (AC8; otherwise `COORDINATOR COVERAGE LOWERED`: stop); `METHOD TryInvokeSink` and `METHOD BuildNotifyFailedMessage` rate at least 90.00; `METHOD HandleToggleClickAsync` uncovered at most `BASELINE-METHOD-HTC-UNCOVERED:` and `METHOD CompletePrime` uncovered at most `BASELINE-METHOD-CP-UNCOVERED:` (changed lines not reduced); both final floors met. Each clause is recorded `MET` or `NOT MET` with its two values; any `NOT MET` stops. -- [ ] [P2-T7] Record the single clean toolchain pass of TaskMaster.sln (P2-T1 to P2-T5) in FEATURE/evidence/qa-gates/toolchain-final-pass.md. +- [x] [P2-T7] Record the single clean toolchain pass of TaskMaster.sln (P2-T1 to P2-T5) in FEATURE/evidence/qa-gates/toolchain-final-pass.md. - Acceptance: the artifact carries `Timestamp:`, `Command:` listing the four CLAUDE.md commands verbatim in order (`dotnet tool run csharpier format .` with `dotnet tool run csharpier check .`; the analyzer `/t:Rebuild`; the `TreatWarningsAsErrors` `/t:Rebuild`; `Invoke-MSTestWithCoverage.ps1`), `EXIT_CODE: 0` and an `Output Summary:` naming each step's artifact and result, the pass number (1, or 2 after the admitted restart), and the D-8 test-step outcome. -- [ ] [P2-T8] Verify the change footprint of the worktree against MERGE-SHA and the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md, and record FEATURE/evidence/qa-gates/footprint-scope.md. +- [x] [P2-T8] Verify the change footprint of the worktree against MERGE-SHA and the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md, and record FEATURE/evidence/qa-gates/footprint-scope.md. - Command: `git -C WORKTREE diff --name-only 981abef77657adcc90d7c116a6b4c6500b79ea29` (MERGE-SHA: the merge of origin/main 993fdd015 into the branch, version 1.4); `git -C WORKTREE status --porcelain --untracked-files=all`; negative controls `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29` and `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29 -- TaskMaster TaskMaster.Test`. - Acceptance, all required: every path in the diff listing and every porcelain entry is one of the eight Write Set code paths, lies under the feature folder, equals the promotion record path, lies under `.claude/agent-memory/` (ambient, never staged), or appears in `INHERITED-PORCELAIN:` of P0-T2; positive control: the union of the two listings contains all eight Write Set code paths (a Write Set code file recorded by an orchestrator phase-boundary commit after MERGE-SHA appears in the diff listing and need not appear in the porcelain listing; an uncommitted created file appears as a `??` entry in the porcelain listing, and an uncommitted modified file appears in both). Any other path is `FOOTPRINT EXCEEDS WRITE SET`: stop. Negative controls, both required: the first control listing is non-empty and contains the line `.gitignore`, a path outside the Write Set, which shows that the path-set check above reports `FOOTPRINT EXCEEDS WRITE SET` when an out-of-scope path differs from the anchor (otherwise `FOOTPRINT CONTROL INERT`: stop); the second control listing is empty, which shows the move of the anchor from BASE-SHA to MERGE-SHA hides no change under TaskMaster/ or TaskMaster.Test/ (otherwise `MERGE TOUCHED ITEM CODE`: stop). Both control listings are recorded in the artifact. - [ ] [P2-T9] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 and record FEATURE/evidence/qa-gates/evidence-hygiene.md. From 2fed92d2f4807653b32ef0236b6f1d82a6084ed5 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 08:20:12 -0400 Subject: [PATCH 10/17] docs(964): record hygiene sweeps, AC1-AC8 check-offs and reduced-audit handoff (P2-T9 to P2-T20) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../evidence/other/ac-status-summary.md | 81 ++++++++++++++++ .../evidence/other/reduced-audit-handoff.md | 92 +++++++++++++++++++ .../evidence/qa-gates/evidence-hygiene.md | 29 ++++++ .../issue.md | 16 ++-- .../plan.2026-10-02T05-20.md | 24 ++--- 5 files changed, 222 insertions(+), 20 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/ac-status-summary.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/reduced-audit-handoff.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/evidence-hygiene.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/ac-status-summary.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/ac-status-summary.md new file mode 100644 index 000000000..146e6f8ed --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/ac-status-summary.md @@ -0,0 +1,81 @@ +# AC Status Summary (P2-T10 to P2-T17) + +Timestamp: 2026-10-03T08-16 +Task: P2-T10 to P2-T17 +Command: Read of the cited evidence artifacts and source files; check-off edits `- [ ] ACn (` to `- [x] ACn (` in docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md +EXIT_CODE: 0 + +Output Summary: +- Source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md, section `## Acceptance Criteria`. +- Per-criterion results are appended below, one task at a time. + +## AC1 (P2-T10) + +AC1: MET +- P1-T14 (FEATURE/evidence/regression-testing/refusal-path-fail-before.md): `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` = Failed; its message carries both fragments `a throwing notification sink must not escape the refusal path` and `notify sink failed`. +- P1-T24 (FEATURE/evidence/regression-testing/refusal-path-pass-after.md): the same test = Passed. +- P2-T5 (FEATURE/evidence/qa-gates/coverage-final.md): FAILED-FQN-COUNT 0, so no FAILED-FQN row equals `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow`. +- issue.md: `- [ ] AC1 (` changed to `- [x] AC1 (`; no other text changed. + +## AC2 (P2-T11) + +AC2: MET +- P1-T14: `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing` = Failed for its recorded reason (`threw exception` and `notify sink failed`); `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` = Failed for its recorded reason (`the refusal path contains a failure of both sinks` and `notify sink failed`). +- P1-T24: both tests = Passed (COUNTERS total=43 passed=43 failed=0). +- issue.md: `- [ ] AC2 (` changed to `- [x] AC2 (`; no other text changed. + +## AC3 (P2-T12) + +AC3: MET +- P1-T22 (FEATURE/evidence/qa-gates/production-edit-scope.md): `catch(` = 2, `catch(Exception)` = 0, `TryInvokeSink(` = 5; the four call-site tokens (`TryInvokeSink(()=>_notifyUnavailable(BuildUnavailableMessage(engineName)),outvarnotifyFailure)`, `TryInvokeSink(()=>_logError(BuildNotifyFailedMessage(engineName),notifyFailure),out_)`, `TryInvokeSink(()=>_logError(BuildToggleFailedMessage(engineName),ex),out_)`, `failure),out_)){_reportedPrimeFaults[reportKey]=0;}`) = 1 each. +- `CATCH-SITES:` names `HandleToggleClickAsync` (lines 208 to 211) and `TryInvokeSink` (lines 295 to 299) only. +- P1-T25 (FEATURE/evidence/qa-gates/test-partials-unchanged.md): `UNCHANGED TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.ThrowingSink.cs exit=0`. +- P1-T24: the four ThrowingSink tests (`GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime`, `GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime`, `GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared`, `HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport`) = Passed. +- issue.md: `- [ ] AC3 (` changed to `- [x] AC3 (`; no other text changed. + +## AC4 (P2-T13) + +AC4: MET +- P1-T8 (FEATURE/evidence/regression-testing/split-fixture-green.md): COUNTERS total=39 passed=39 failed=0; all 11 INVARIANT-NAMES = Passed. +- P1-T24: COUNTERS total=43 passed=43 failed=0; all 11 INVARIANT-NAMES = Passed; `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` = Passed. +- P1-T25: every clause met (four partials `exit=0`, Race `NON-DOC-CHANGES` 0, primary fixture single MINUS `message => Notifications.Add(message),`). +- P1-T22: all 14 SPAN-HASH lines `equal=True`; `failure),out_)){_reportedPrimeFaults[reportKey]=0;}` = 1. +- issue.md: `- [ ] AC4 (` changed to `- [x] AC4 (`; no other text changed. + +## AC5 (P2-T14) + +AC5: MET +- P1-T22: `The in-flight` = 0, `most recently completed` = 0, `The prime task, or` = 0; `The registration marker for an engine key` = 1, `The marker is not the prime task itself` = 1, `The registered marker, or` = 1 (coordinator-run phrase census, recorded in production-edit-scope.md). +- Read of TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs lines 10 to 26: the `GetPrimeTask` `` and `` match delivered source F5 verbatim and describe the registration marker, completed only after the prime outcome has been observed and reported. +- issue.md: `- [ ] AC5 (` changed to `- [x] AC5 (`; no other text changed. + +## AC6 (P2-T15) + +AC6: MET +- P1-T26 (FEATURE/evidence/qa-gates/file-line-counts.md): every clause met: PRODUCTION-FILES 3 (302, 197, 86 lines, each at most 450); TEST-PARTIALS 7 (largest 481, each at most 500); 3 production and 7 test compile entries, each LINES file registered exactly once. +- P1-T7 (FEATURE/evidence/regression-testing/split-census.md): census verdict PASS (split is a pure move). +- P2-T3: CSC_OUT_TASKMASTER 2, ERRORS 0. P2-T4: CSC_OUT_TASKMASTER 2, ERRORS 0. +- issue.md: `- [ ] AC6 (` changed to `- [x] AC6 (`; no other text changed. + +## AC7 (P2-T16) + +AC7: MET +- P1-T22: every remaining PHRASES-DOC entry holds its final value (all 24 rows MATCH; coordinator-run phrase census recorded in production-edit-scope.md). +- Read of TaskMaster/Ribbon/EngineToggleStateCoordinator.cs: constructor parameter docs `enginesAccessor` (87 to 93), `notifyUnavailable` (98 to 103) and `logError` (104 to 108) match F1; the `HandleToggleClickAsync` summary (160 to 164) and remarks (170 to 183) match F3. The code they describe holds: the refusal path routes the notification and the follow-up log call through `TryInvokeSink` (188 to 199), and the click boundary `catch` (208 to 211) routes its log call through `TryInvokeSink`; the only other `catch` is in `TryInvokeSink` (295 to 299). +- Read of TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs: the `StartObservedPrime` remarks (71 to 82) match F6 and name the two `catch` clauses that exist; the `CompletePrime` summary (131 to 137) and remarks (138 to 164) match F7a and F7b, and the code holds: the record `_reportedPrimeFaults[reportKey] = 0;` (190) is the only statement of the branch taken when `TryInvokeSink` returns `true` (183 to 191). +- issue.md: `- [ ] AC7 (` changed to `- [x] AC7 (`; no other text changed. + +## AC8 (P2-T17) + +AC8: MET +- P2-T7 (FEATURE/evidence/qa-gates/toolchain-final-pass.md): one clean pass (pass 1) of all four steps. +- P2-T5: runner exit code 0 (no failing test; no pre-existing FAILED-FQN values), LINE-FLOOR MET (85.96%), BRANCH-FLOOR MET (80.10%). +- P2-T6: FINAL-COORD-LINE-RATE 100 at least BASELINE-COORD-LINE-RATE 100. +- issue.md: `- [ ] AC8 (` changed to `- [x] AC8 (`; no other text changed. + +### Acceptance Criteria Status +- Source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md +- Total AC items: 8 +- Checked off (delivered): 8 +- Remaining (unchecked): 0 +- Items remaining: none diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/reduced-audit-handoff.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/reduced-audit-handoff.md new file mode 100644 index 000000000..eaab95508 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/reduced-audit-handoff.md @@ -0,0 +1,92 @@ +# Reduced (Minor) Audit Handoff (P2-T18, P2-T19) + +Timestamp: 2026-10-03T08-18 +Task: P2-T18 +Command: Record the reduced-audit handoff for issue #964 (no command executed; the audit itself is delegated to feature-review by the coordinator) +EXIT_CODE: 0 + +Output Summary: +- Work mode: minor-audit. AC source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md (section `## Acceptance Criteria`). +- AC status (copied from FEATURE/evidence/other/ac-status-summary.md): total 8, checked off 8, remaining 0, items remaining none. +- Handoff target: feature-review (reduced audit), delegated by the coordinator after this plan completes. This executor does not perform the audit. + +## Evidence paths + +Phase 0 (FEATURE/evidence/baseline/): +- phase0-instructions-read.md (P0-T1) +- scope-and-anchor.md (P0-T2) +- anchor-production.md (P0-T3, P0-T4) +- anchor-test-side.md (P0-T5) +- bootstrap-sdk.md (P0-T6) +- bootstrap-tool-restore.md (P0-T7) +- bootstrap-nuget-restore.md (P0-T8) +- bootstrap-dotnet-coverage.md (P0-T9) +- csharpier-check-baseline.md (P0-T10) +- msbuild-analyzer-baseline.md (P0-T11) +- msbuild-nullable-baseline.md (P0-T12) +- coordinator-tests-baseline.md (P0-T13) +- coverage-baseline.md (P0-T14) + +Phase 1: +- FEATURE/evidence/other/implementation-handoff.md (P1-T1) +- FEATURE/evidence/regression-testing/split-census.md (P1-T5, P1-T6, P1-T7) +- FEATURE/evidence/regression-testing/split-fixture-green.md (P1-T8) +- FEATURE/evidence/regression-testing/sink-guard-partial-tokens.md (P1-T11, P1-T13) +- FEATURE/evidence/regression-testing/refusal-path-fail-before.md (P1-T14, expect-fail) +- FEATURE/evidence/qa-gates/production-edit-scope.md (P1-T21, P1-T22) +- FEATURE/evidence/regression-testing/refusal-path-pass-after.md (P1-T23, P1-T24) +- FEATURE/evidence/qa-gates/test-partials-unchanged.md (P1-T25) +- FEATURE/evidence/qa-gates/file-line-counts.md (P1-T26) + +Phase 2 (FEATURE/evidence/qa-gates/ unless stated): +- csharpier-format.md (P2-T1) +- csharpier-check-final.md (P2-T2) +- msbuild-analyzer-final.md (P2-T3) +- msbuild-nullable-final.md (P2-T4) +- coverage-final.md (P2-T5) +- coverage-comparison.md (P2-T6) +- toolchain-final-pass.md (P2-T7) +- footprint-scope.md (P2-T8) +- evidence-hygiene.md (P2-T9, with the P2-T20 FINAL-SWEEP appended) +- FEATURE/evidence/other/ac-status-summary.md (P2-T10 to P2-T17) +- FEATURE/evidence/other/reduced-audit-handoff.md (P2-T18, P2-T19; this file) + +Preparation-phase record (not written by any task of this plan): FEATURE/evidence/other/preflight-clearance.2026-10-02T07-50.md. + +## Folded-in related defects (D-7) + +- D-7a: the `.Race.cs` remark claimed the re-prime "logs a second error", which #948 made false; reworded (remark only, no code change; P1-T12, verified by P1-T25 `NON-DOC-CHANGES` 0). +- D-7b: the `GetPressed` returns sentence and the `logError` parameter doc now state the accessor's non-throwing precondition and the guarded behaviour (F1, F2). +- D-7c: the missing throwing-sink-leaves-report-owed test, `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` (GUARD-NAME), added in the SinkGuard partial; Passed at P1-T14 and P1-T24. + +## Commits (D-10) + +- No task of this plan created a commit or staged a file. +- Every commit after MERGE-SHA 981abef77657adcc90d7c116a6b4c6500b79ea29 is an orchestrator phase-boundary commit made between tasks at the coordinator's direction: 98934d356, 0561003e9, 08511aa6e, 1b0304f88, 9de37caf1, ca215e068, fc1624489, f179a4426. + +## Reduced artifact checks for the auditor + +- Fail-before: FEATURE/evidence/regression-testing/refusal-path-fail-before.md (EXIT_CODE 1, ExpectedExitCode 1; the three FAIL-BEFORE-NAMES failed for their recorded reasons). +- Pass-after: FEATURE/evidence/regression-testing/refusal-path-pass-after.md (43 of 43 passed, VSTEST_EXIT_CODE 0). +- Coverage comparison: FEATURE/evidence/qa-gates/coverage-comparison.md (coordinator line rate 100 at baseline and final; new methods 100; every clause MET). +- Footprint gate: FEATURE/evidence/qa-gates/footprint-scope.md (within the Write Set; both negative controls hold). +- Hygiene gate: FEATURE/evidence/qa-gates/evidence-hygiene.md (P2-T9 and its P2-T20 final sweep). + +## Notes for the auditor + +- The P1-T22 phrase census (CMD-PHRASE-COUNT, PHRASES-DOC) was run by the coordinator under the maintainer's second one-time bypass of enforce-promotion-mcp-only.ps1 (2026-10-03) at HEAD ca215e068 and recorded verbatim with provenance in production-edit-scope.md; the executor compared every row against the required final values (24 of 24 MATCH) and ran CMD-STRIPPED-COUNT and CMD-PROTECTED-SPANS itself. + +## P2-T19 + +Timestamp: 2026-10-03T08-18 +Task: P2-T19 +Command: Read of the plan `## Write Set`; P2-T8 union listing; Glob tool `**/*` over FEATURE/evidence; Glob tool `preflight-clearance.*.md` over FEATURE/evidence/other; git hash-object docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md +EXIT_CODE: 0 + +WRITE-SET-CONFIRMATION: +- Code paths: the P2-T8 union listing contains exactly eight code paths (TaskMaster/Ribbon/EngineToggleStateCoordinator.cs, TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs, TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs, TaskMaster/TaskMaster.csproj, TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs, TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs, TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs, TaskMaster.Test/TaskMaster.Test.csproj); each is named in the `## Write Set` code list, and each of the eight code entries appears in that union. +- Feature documents: issue.md (eight check-off edits only, 8 lines changed) and the plan (task check-off edits only). +- Evidence files: the Glob tool over FEATURE/evidence returned 34 files: all 13 baseline, 5 regression-testing, 12 qa-gates and 3 other files named in the `## Write Set` evidence list exist, plus the preparation-phase record. No other file exists under FEATURE/evidence. +- Preparation-phase record: the Glob tool over FEATURE/evidence/other for `preflight-clearance.*.md` returned exactly one path, which in repository-relative forward-slash form is docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/preflight-clearance.2026-10-02T07-50.md and equals `CLEARANCE-PATH:` of P0-T2. +- CLEARANCE-BLOB-FINAL: 2b1223012d7bd3551f7d9e2c9aa5539156a23cd2 (equals `CLEARANCE-BLOB:` of P0-T2). +- Discrepancies: none. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/evidence-hygiene.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/evidence-hygiene.md new file mode 100644 index 000000000..211eb8439 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/evidence-hygiene.md @@ -0,0 +1,29 @@ +# Evidence Hygiene (P2-T9, P2-T20) + +## P2-T9 + +Timestamp: 2026-10-03T08-16 +Task: P2-T9 +Command: CMD-HYGIENE over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 (host tokens derived at run time, not recorded) +EXIT_CODE: 0 + +Output Summary: +- FILES_SCANNED=33 ACCOUNT_HITS=0 MACHINE_HITS=0 DRIVE_USERS_HITS=0 RAW_DOCUMENTS=0 +- No HIT-FILE row. +- FILES_SCANNED meets the lower bound of 33 (issue.md and the plan, 2; 13 baseline, 5 regression-testing and 11 qa-gates evidence files, 29; implementation-handoff.md, 1; the preparation-phase record, 1). +- Verdict: PASS (no repair required; no PREPARATION RECORD HOST HIT). + +## P2-T20 + +FINAL-SWEEP: + +Timestamp: 2026-10-03T08-19 +Task: P2-T20 +Command: CMD-HYGIENE over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 (re-run after the AC check-offs and the audit handoff) + +Output Summary: +- FILES_SCANNED=36 ACCOUNT_HITS=0 MACHINE_HITS=0 DRIVE_USERS_HITS=0 RAW_DOCUMENTS=0 +- No HIT-FILE row. +- FILES_SCANNED meets the lower bound of 36 (issue.md and the plan, 2; 33 Write Set evidence files; the preparation-phase record, 1). +- This task created no new evidence file, so the P2-T19 evidence-set confirmation still holds. +- Verdict: PASS. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md index 4dab5ba7e..03b4144eb 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md @@ -24,14 +24,14 @@ Re-measured on origin/main at 942873699 (2026-10-02): the file is 496 of 500 lin Each criterion below is falsifiable by the named test or measurement. Related defects in the same file are folded in under the 2026-10-02 related-defect remediation directive (criteria AC6 and AC7). -- [ ] AC1 (refusal-path notification guard, regression-first): with the engines accessor returning null and a `notifyUnavailable` sink that throws, `EngineToggleStateCoordinator.HandleToggleClickAsync` completes without throwing. A new MSTest regression test proves this; it is recorded failing against the unmodified coordinator (fail-before evidence under `evidence/regression-testing/`) and passing after the fix. -- [ ] AC2 (notification failure is reported, not lost): in the AC1 scenario the notification sink is attempted exactly once, the sink's exception is delivered exactly once to `logError` (the same exception instance), no engine member is invoked, and no control is invalidated. When `logError` also throws in that scenario, `HandleToggleClickAsync` still completes without throwing. Both behaviours are asserted by named MSTest tests. -- [ ] AC3 (one shared sink guard): all three sink call sites (refusal-path `notifyUnavailable`, click-boundary `logError`, prime-fault `logError` in `CompletePrime`) route through a single private guard helper; no other `catch` clause that discards a sink exception remains in the coordinator's source files. Verified by reading the split source and by the existing #947 tests in `EngineToggleStateCoordinatorTests.ThrowingSink.cs` passing unchanged. -- [ ] AC4 (prime-marker ordering invariants preserved across the split): the #942/#944/#947/#948 invariants still hold: the marker is registered before the prime starts; a prime fault is reported before the marker is cleared; a sink that throws leaves the fault kind unrecorded (report still owed); a repeated fault kind for the same engine is reported once. Verified by every existing test in the `EngineToggleStateCoordinatorTests` partials (`.cs`, `.Race.cs`, `.PrimeFaultOrdering.cs`, `.PrimeRegistration.cs`, `.ThrowingSink.cs`, `.RepeatFaultSuppression.cs`) passing with no assertion weakened or removed. -- [ ] AC5 (`GetPrimeTask` documentation accuracy): the `GetPrimeTask` `` and `` describe the registration marker (completed only after the prime outcome has been observed and reported), not "the prime task" or "the in-flight prime". Verified by reading the doc comment. -- [ ] AC6 (file-size split): `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` is split into cohesive partial-class files of the same `internal sealed partial class EngineToggleStateCoordinator`, each file at or below 450 lines, each new file registered as a `Compile` item in `TaskMaster/TaskMaster.csproj`; every touched test file stays at or below 500 lines. Verified by a line count of every coordinator source file and test file and by the build compiling the new files. -- [ ] AC7 (comment drift in touched files): every comment that counts or locates the coordinator's `catch` clauses (the `HandleToggleClickAsync` summary and remarks, the `StartObservedPrime` remarks, the `CompletePrime` remarks) and the `HandleToggleClickAsync` "never throws" remark match the post-change code, and the constructor's `notifyUnavailable` and `enginesAccessor` parameter docs state the guarded behaviour and the accessor's non-throwing precondition. Verified by reading each named comment against the code. -- [ ] AC8 (toolchain and coverage): the CLAUDE.md C# toolchain passes in one clean pass (csharpier check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild` without `/p:Nullable=enable`, `Invoke-MSTestWithCoverage.ps1`), with no new failing test relative to baseline and the coordinator's line coverage not lower than its baseline figure. +- [x] AC1 (refusal-path notification guard, regression-first): with the engines accessor returning null and a `notifyUnavailable` sink that throws, `EngineToggleStateCoordinator.HandleToggleClickAsync` completes without throwing. A new MSTest regression test proves this; it is recorded failing against the unmodified coordinator (fail-before evidence under `evidence/regression-testing/`) and passing after the fix. +- [x] AC2 (notification failure is reported, not lost): in the AC1 scenario the notification sink is attempted exactly once, the sink's exception is delivered exactly once to `logError` (the same exception instance), no engine member is invoked, and no control is invalidated. When `logError` also throws in that scenario, `HandleToggleClickAsync` still completes without throwing. Both behaviours are asserted by named MSTest tests. +- [x] AC3 (one shared sink guard): all three sink call sites (refusal-path `notifyUnavailable`, click-boundary `logError`, prime-fault `logError` in `CompletePrime`) route through a single private guard helper; no other `catch` clause that discards a sink exception remains in the coordinator's source files. Verified by reading the split source and by the existing #947 tests in `EngineToggleStateCoordinatorTests.ThrowingSink.cs` passing unchanged. +- [x] AC4 (prime-marker ordering invariants preserved across the split): the #942/#944/#947/#948 invariants still hold: the marker is registered before the prime starts; a prime fault is reported before the marker is cleared; a sink that throws leaves the fault kind unrecorded (report still owed); a repeated fault kind for the same engine is reported once. Verified by every existing test in the `EngineToggleStateCoordinatorTests` partials (`.cs`, `.Race.cs`, `.PrimeFaultOrdering.cs`, `.PrimeRegistration.cs`, `.ThrowingSink.cs`, `.RepeatFaultSuppression.cs`) passing with no assertion weakened or removed. +- [x] AC5 (`GetPrimeTask` documentation accuracy): the `GetPrimeTask` `` and `` describe the registration marker (completed only after the prime outcome has been observed and reported), not "the prime task" or "the in-flight prime". Verified by reading the doc comment. +- [x] AC6 (file-size split): `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` is split into cohesive partial-class files of the same `internal sealed partial class EngineToggleStateCoordinator`, each file at or below 450 lines, each new file registered as a `Compile` item in `TaskMaster/TaskMaster.csproj`; every touched test file stays at or below 500 lines. Verified by a line count of every coordinator source file and test file and by the build compiling the new files. +- [x] AC7 (comment drift in touched files): every comment that counts or locates the coordinator's `catch` clauses (the `HandleToggleClickAsync` summary and remarks, the `StartObservedPrime` remarks, the `CompletePrime` remarks) and the `HandleToggleClickAsync` "never throws" remark match the post-change code, and the constructor's `notifyUnavailable` and `enginesAccessor` parameter docs state the guarded behaviour and the accessor's non-throwing precondition. Verified by reading each named comment against the code. +- [x] AC8 (toolchain and coverage): the CLAUDE.md C# toolchain passes in one clean pass (csharpier check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild` without `/p:Nullable=enable`, `Invoke-MSTestWithCoverage.ps1`), with no new failing test relative to baseline and the coordinator's line coverage not lower than its baseline figure. ## Environment diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md index 1d78a77ef..3d37a246e 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md @@ -944,27 +944,27 @@ No code file is edited in Phase 2. If P2-T1 rewrites a Write Set file, the loop - [x] [P2-T8] Verify the change footprint of the worktree against MERGE-SHA and the Write Set of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md, and record FEATURE/evidence/qa-gates/footprint-scope.md. - Command: `git -C WORKTREE diff --name-only 981abef77657adcc90d7c116a6b4c6500b79ea29` (MERGE-SHA: the merge of origin/main 993fdd015 into the branch, version 1.4); `git -C WORKTREE status --porcelain --untracked-files=all`; negative controls `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29` and `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 981abef77657adcc90d7c116a6b4c6500b79ea29 -- TaskMaster TaskMaster.Test`. - Acceptance, all required: every path in the diff listing and every porcelain entry is one of the eight Write Set code paths, lies under the feature folder, equals the promotion record path, lies under `.claude/agent-memory/` (ambient, never staged), or appears in `INHERITED-PORCELAIN:` of P0-T2; positive control: the union of the two listings contains all eight Write Set code paths (a Write Set code file recorded by an orchestrator phase-boundary commit after MERGE-SHA appears in the diff listing and need not appear in the porcelain listing; an uncommitted created file appears as a `??` entry in the porcelain listing, and an uncommitted modified file appears in both). Any other path is `FOOTPRINT EXCEEDS WRITE SET`: stop. Negative controls, both required: the first control listing is non-empty and contains the line `.gitignore`, a path outside the Write Set, which shows that the path-set check above reports `FOOTPRINT EXCEEDS WRITE SET` when an out-of-scope path differs from the anchor (otherwise `FOOTPRINT CONTROL INERT`: stop); the second control listing is empty, which shows the move of the anchor from BASE-SHA to MERGE-SHA hides no change under TaskMaster/ or TaskMaster.Test/ (otherwise `MERGE TOUCHED ITEM CODE`: stop). Both control listings are recorded in the artifact. -- [ ] [P2-T9] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 and record FEATURE/evidence/qa-gates/evidence-hygiene.md. +- [x] [P2-T9] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 and record FEATURE/evidence/qa-gates/evidence-hygiene.md. - Acceptance: `FILES_SCANNED=` at least 33 (derivation: issue.md and this plan, 2; the 13 baseline, 5 regression-testing and 11 qa-gates evidence files of the Write Set other than evidence-hygiene.md, which this task writes after the sweep, 29; implementation-handoff.md, 1; the preparation-phase record, whose presence P0-T2 established, 1; 2 + 29 + 1 + 1 = 33), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of `CLEARANCE-PATH:` is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report; a non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder (the original stays under `coverage/`). -- [ ] [P2-T10] Check off AC1 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` per acceptance-criteria-tracking and append `AC1: MET` or `AC1: NOT MET` with its evidence to FEATURE/evidence/other/ac-status-summary.md (this task creates the file). +- [x] [P2-T10] Check off AC1 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` per acceptance-criteria-tracking and append `AC1: MET` or `AC1: NOT MET` with its evidence to FEATURE/evidence/other/ac-status-summary.md (this task creates the file). - Acceptance: AC1 is checked (`- [ ] AC1 (` becomes `- [x] AC1 (`, no other text changed) only when P1-T14 shows `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` Failed with its two fragments, P1-T24 shows it Passed, and no P2-T5 `FAILED-FQN` row equals `TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests.HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow`; otherwise it stays unchecked with the failing values recorded. -- [ ] [P2-T11] Check off AC2 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P2-T11] Check off AC2 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: AC2 is checked only when P1-T24 shows `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing` and `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` Passed and P1-T14 shows both Failed for their recorded reasons; otherwise unchecked. -- [ ] [P2-T12] Check off AC3 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P2-T12] Check off AC3 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: AC3 is checked only when P1-T22 shows `"catch("` 2, `"catch(Exception)"` 0, `"TryInvokeSink("` 5 and each of the four call-site tokens 1, `CATCH-SITES:` names `HandleToggleClickAsync` and `TryInvokeSink` only, and P1-T25 shows the ThrowingSink partial `exit=0` with its four tests Passed at P1-T24; otherwise unchecked. -- [ ] [P2-T13] Check off AC4 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P2-T13] Check off AC4 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: AC4 is checked only when P1-T8 and P1-T24 show every `INVARIANT-NAMES` entry Passed with `failed=0`, P1-T24 shows `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` Passed, P1-T25 met every clause, and P1-T22 shows every `SPAN-HASH` `equal=True` and `"failure),out_)){_reportedPrimeFaults[reportKey]=0;}"` 1; otherwise unchecked. -- [ ] [P2-T14] Check off AC5 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P2-T14] Check off AC5 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: AC5 is checked only when P1-T22 shows `"The in-flight"`, `"most recently completed"` and `"The prime task, or"` 0 and `"The registration marker for an engine key"`, `"The marker is not the prime task itself"` and `"The registered marker, or"` 1, and a Read of the `GetPrimeTask` summary and returns in `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` confirms the text of delivered source F5; otherwise unchecked. -- [ ] [P2-T15] Check off AC6 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P2-T15] Check off AC6 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: AC6 is checked only when P1-T26 met every clause, P1-T7 met its census, and P2-T3 and P2-T4 show `CSC_OUT_TASKMASTER:` at least 1 with `ERRORS: 0`; otherwise unchecked. -- [ ] [P2-T16] Check off AC7 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P2-T16] Check off AC7 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: AC7 is checked only when every remaining `PHRASES-DOC` entry of P1-T22 holds its final value and a Read of the `HandleToggleClickAsync` summary and remarks, the `StartObservedPrime` remarks, the `CompletePrime` summary and remarks and the three constructor parameter docs confirms the delivered texts F1, F3, F6 and F7 against the code the comments describe; otherwise unchecked. -- [ ] [P2-T17] Check off AC8 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md, followed by the AC status summary block of acceptance-criteria-tracking (source, total 8, checked, remaining, items remaining). +- [x] [P2-T17] Check off AC8 in `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` and append the result to FEATURE/evidence/other/ac-status-summary.md, followed by the AC status summary block of acceptance-criteria-tracking (source, total 8, checked, remaining, items remaining). - Acceptance: AC8 is checked only when P2-T7 records one clean pass of all four steps, P2-T5 met the D-8 test-step rule and both floors, and P2-T6 shows `FINAL-COORD-LINE-RATE` at least `BASELINE-COORD-LINE-RATE`; the AC8 line in the summary states the runner exit code and, when it is non-zero, the pre-existing `FAILED-FQN` values. Otherwise unchecked. -- [ ] [P2-T18] Hand off for the reduced (minor) audit and record FEATURE/evidence/other/reduced-audit-handoff.md. +- [x] [P2-T18] Hand off for the reduced (minor) audit and record FEATURE/evidence/other/reduced-audit-handoff.md. - Acceptance: the artifact carries `Timestamp:`, the AC source (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`), the AC status counts copied from ac-status-summary.md, the evidence paths of every Phase 0, Phase 1 and Phase 2 artifact, the D-7 list of folded-in related defects, the statement that no task of this plan created a commit or staged a file (D-10), any commit after MERGE-SHA being an orchestrator phase-boundary commit, and the reduced artifact checks for the auditor: the fail-before artifact, the pass-after artifact, the coverage comparison, the footprint gate and the hygiene gate (P2-T9 and its P2-T20 final sweep). -- [ ] [P2-T19] Confirm the `## Write Set` section of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md enumerates every file the execution created or modified, and append the confirmation to FEATURE/evidence/other/reduced-audit-handoff.md under `WRITE-SET-CONFIRMATION:`. +- [x] [P2-T19] Confirm the `## Write Set` section of docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/plan.2026-10-02T05-20.md enumerates every file the execution created or modified, and append the confirmation to FEATURE/evidence/other/reduced-audit-handoff.md under `WRITE-SET-CONFIRMATION:`. - Acceptance: every code path of P2-T8's union listing is named in the `## Write Set` code list and every one of the eight code entries appears in that union; every evidence file named in the `## Write Set` evidence list exists on disk (Glob tool over FEATURE/evidence); the Glob tool over FEATURE/evidence/other for `preflight-clearance.*.md` returns exactly one path which, converted to repository-relative forward-slash form, equals `CLEARANCE-PATH:` of P0-T2, and `git -C WORKTREE hash-object CLEARANCE-PATH` (a working-tree hash, so the check holds whether or not anything has been committed since P0-T2) prints the `CLEARANCE-BLOB:` value of P0-T2, recorded as `CLEARANCE-BLOB-FINAL:`; and no other file exists under FEATURE/evidence, so any file named neither in the evidence list nor as the preparation-phase record is a discrepancy. A discrepancy is recorded and leaves this task unchecked. -- [ ] [P2-T20] Re-run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 after the AC check-offs and the audit handoff, and append the output under `FINAL-SWEEP:` to FEATURE/evidence/qa-gates/evidence-hygiene.md. +- [x] [P2-T20] Re-run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 after the AC check-offs and the audit handoff, and append the output under `FINAL-SWEEP:` to FEATURE/evidence/qa-gates/evidence-hygiene.md. - Acceptance: `FILES_SCANNED=` at least 36 (derivation: issue.md and this plan, 2, plus the 33 evidence files of the Write Set — 13 baseline, 5 regression-testing, 12 qa-gates and 3 other — and the preparation-phase record, all of which exist once P2-T19 has passed; 2 + 33 + 1 = 36), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of `CLEARANCE-PATH:` is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report, because P2-T19 requires that record unchanged; a non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder. This task creates no new evidence file, so the P2-T19 evidence-set confirmation still holds. From 96d2975d40c95b15619d5ff3b164d5c5acafc038 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 08:43:03 -0400 Subject: [PATCH 11/17] docs(964): add reduced-audit policy, code-review and feature-audit artifacts Review verdict PASS with 0 blocking findings. CR-1 and CR-4 are related non-blocking findings and are remediated in this item under the related-defect directive. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../code-review.2026-10-03T08-50.md | 98 +++++++ .../feature-audit.2026-10-03T08-50.md | 81 +++++ .../policy-audit.2026-10-03T08-50.md | 276 ++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T08-50.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T08-50.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md new file mode 100644 index 000000000..2b2718073 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md @@ -0,0 +1,98 @@ +# Code Review: engine-toggle-coordinator-947-review-residuals (Issue #964) + +- Date: 2026-10-03 (review label `2026-10-03T08-50`; derived from the worktree reflog, see the policy audit) +- Branch: `bug/engine-toggle-coordinator-947-review-residuals-964`, head `2fed92d2f4807653b32ef0236b6f1d82a6084ed5`, base origin/main `993fdd01566dee82e5f37acb761a600feaaa1454` (merged into the item at `981abef77`) +- Scope reviewed: the full origin/main..HEAD diff: `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (M), `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (A), `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (A), `TaskMaster/TaskMaster.csproj` (M), `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (M), `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (A), `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (M), `TaskMaster.Test/TaskMaster.Test.csproj` (M), plus the feature folder and the promoted record. +- Method (no Bash): the three production files, the primary fixture, the SinkGuard partial and the ThrowingSink partial were read in full in the item worktree; the caller's verbatim diff was read in full; the Race partial was read around the changed remark; `RibbonCommandBoundary.ReportFailure` and `SafeLog` were read to verify the precedent the new remarks cite; the gitignored raw Cobertura documents were read at the root and at the three coordinator `` nodes; the worktree reflog supplied head and clock. + +## Executive Summary + +**Verdict: PASS.** 0 Blocking findings; 4 Non-blocking findings (CR-1 Minor, CR-2 to CR-4 Informational); 5 observations (O-1 to O-5). + +The change does what the three #947 residuals asked and nothing beyond it. The 496-line coordinator is split into three cohesive partials by a pure move (the executor's ordinal multiset census shows only the eight structural lines a split necessarily introduces, and this review found no statement that changed position relative to its neighbours), then the fix is applied: a single `private static bool TryInvokeSink(Action sinkCall, out Exception sinkFailure)` replaces the two copy-pasted guard blocks of #947 and additionally wraps the previously unguarded refusal-path notification. The helper's shape is the right one for three callers with three different follow-ups: the refusal path forwards a notification failure to the log sink through a second guarded call, the click boundary discards a log failure, and `CompletePrime` records the reported fault kind only when the helper returns `true`, which keeps the #948 record-after-sink placement without a `try` in `CompletePrime`. All four ordering invariants from #942, #944, #947 and #948 are visibly intact in the moved code. The `GetPrimeTask` documentation now describes the registration marker. Every comment that counts the type's `catch` clauses was rewritten and is accurate against the two that exist. The four new tests are deterministic, reuse the fixture's `Harness` with one small hook, and the three refusal-path tests demonstrably fail without the fix. + +The one finding worth acting on in this item (CR-1) is a pre-existing coverage gap that the split makes visible: `RenderEngineName`'s null-or-empty arm has never been exercised, and because the move leaves it as the only branch in the new Messages partial, that file's Cobertura node reads 50% branches while the type as a whole reads 97.73%. One refusal-path test with a null engine key closes it. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Non-blocking (Minor) | `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` | line 20 (`RenderEngineName`), reached from `BuildUnavailableMessage` (26-35) and `BuildUnmappedKeyMessage` (77-84) | The true arm of `string.IsNullOrEmpty(engineName) ? NullEngineNameToken : engineName` is never executed by any test: the refusal-path and unmapped-key tests all pass `"Spam"` or `"NotAToggleBackedEngine"`. The arm is reachable in production (`HandleToggleClickAsync(null)` with the engines unavailable renders `(null)` into the notification; `ExecuteToggleAsync(null)` renders it into the `ArgumentException`). The gap pre-dates this item (the pre-split node read 39/40 branches with this same arm uncovered), but the split isolates it: the Messages partial's node now reads line-rate 1, branch-rate 0.5. | Add one test to the SinkGuard partial (or the primary fixture if its headroom permits, see O-1): engines unavailable, `HandleToggleClickAsync(null)` does not throw, `Notifications` contains a single message containing `"(null)"`, `Errors` is empty, `Engines.VerifyNoOtherCalls()`. A `[DataRow(null)] [DataRow("")]` pair covers both inputs the token exists for. | General Unit Test Policy UT2 (negative flows for invalid or missing inputs) and the new-file branch threshold in `.claude/rules/quality-tiers.md`; the `NullEngineNameToken` constant exists precisely for this input and is otherwise untested. Non-blocking because the item's own added lines and branches are 100% covered, the type's aggregate is 43/44, and nothing regressed. Related to this item (same files, same component), so it falls under the coordinator's related-defect rule. | Final Cobertura `` nodes: `EngineToggleStateCoordinator.cs` branch-rate 1, `Prime.cs` branch-rate 1, `Messages.cs` branch-rate 0.5; baseline single node branch-rate 0.975; `COORD-BRANCHES covered=43 valid=44` in evidence/qa-gates/coverage-final.md; Read of every test that reaches a builder. | +| Non-blocking (Informational) | `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` | lines 287-300 (`TryInvokeSink`) | `catch (Exception ex)` contains every exception a sink throws, including those a boundary would normally not want to contain (for example `OutOfMemoryException`). | No change. The precedent `RibbonCommandBoundary.SafeLog` (lines 103-113) has the identical shape; the sink is the last reporting channel and the caller is an `async void` Office handler, so an escaping exception is strictly worse than a contained one. Recorded as accepted exception X-1 in the policy audit. | CLAUDE.md C#4 admits a broad catch at a clear boundary with documentation; both are present (remarks lines 278-286 and the summary's "contains any exception it throws"). | Read of lines 264-300 and of `RibbonCommandBoundary.cs` lines 80-113. | +| Non-blocking (Informational) | `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` | lines 188-199 | The refusal path builds two closures per refused click (the notification lambda and, on failure, the forwarding lambda capturing `notifyFailure`). | No change. A refused click is a user-paced event on the pre-`SetGlobals` window; the allocation is negligible and the lambda form is what makes the single guard reusable across three differently-typed sinks. | General Code Change Policy 6.1 (clarity first; optimise only on demonstrated need). | Read of lines 184-212. | +| Non-blocking (Informational) | `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` | lines 88-112 (`HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`) | The both-sinks-throw test asserts the notification and log attempts and the forwarded exception instance but, unlike its sibling at lines 52-79, does not assert `Engines.VerifyNoOtherCalls()` or `Invalidations.Should().BeEmpty()`. | Optional: add the two assertions for symmetry. The sibling already pins both facts for the same arrange, so no coverage of behaviour is lost either way. | UT3 (clear intent); scenario completeness is met by the pair taken together. | Read of lines 52-112. | + +## Detailed Review + +### The split (`EngineToggleStateCoordinator.cs` -> `.cs`, `.Prime.cs`, `.Messages.cs`) + +**Cohesion.** The main file keeps the type's contract (class remarks, fields, constructor, `GetPressed`, `HandleToggleClickAsync`, `ExecuteToggleAsync`) and the sink guard that the click boundary calls directly. `Prime.cs` holds the prime lifecycle in call order (`GetPrimeTask`, `StartPrimeIfNeeded`, `StartObservedPrime`, `ApplyPrimeAsync`, `CompletePrime`). `Messages.cs` holds the token, `RenderEngineName` and the five builders. Each partial has one purpose and a trimmed `using` set (`System.Globalization` travels with the builders, `System.Threading` with the continuation options). Line counts 302 / 197 / 86, all under the AC6 ceiling of 450 and well under 500. + +**Pure move.** Verified three ways: the executor's ordinal multiset census (`evidence/regression-testing/split-census.md`: exactly `{` x4, `}` x4, `namespace TaskMaster` x2, `using System;` x2, `using System.Threading.Tasks;` x1, `using UtilitiesCS;` x1, the `sealed class` line replaced by three `sealed partial class` lines); the fourteen `SPAN-HASH equal=True` rows over every protected signature and field declaration (`evidence/qa-gates/production-edit-scope.md`); and this review's reading of the caller's verbatim diff, in which every moved hunk is a deletion in the main file paired with an identical insertion in a partial, apart from the documented fix and documentation edits. Both project files register the new partials in the existing Ribbon group (`TaskMaster.csproj` 467-468; `TaskMaster.Test.csproj` 364), and the final coverage document carries one `` node per file, which is the compile-and-discovery proof. + +### The fix: one guard, three call sites + +**`TryInvokeSink` (main file 264-300).** `private static bool` with `out Exception sinkFailure`; `sinkFailure = null; return true` on a normal return, `sinkFailure = ex; return false` on a throw; never rethrows. The contract is documented (summary, both parameters, returns, remarks). The remarks correctly describe the division of responsibility: the helper contains, the caller decides. This is the `RibbonCommandBoundary.ReportFailure` pattern (log first, then present, and a presentation failure goes back to the log) expressed as one reusable primitive rather than as nested `try` blocks. + +**Refusal path (main file 186-202).** `if (!TryInvokeSink(() => _notifyUnavailable(BuildUnavailableMessage(engineName)), out var notifyFailure)) { _ = TryInvokeSink(() => _logError(BuildNotifyFailedMessage(engineName), notifyFailure), out _); } return;` The notification is attempted exactly once; its failure, if any, reaches the log sink exactly once with the same exception instance and a dedicated message naming the engine; a log-sink failure is discarded; no engine member is touched. This is the #947 residual 1 and it is closed. The `_enginesAccessor()` call at 186 remains outside any guard by design, and the constructor documentation now states the non-throwing precondition (88-90) and the `GetPressed` and `HandleToggleClickAsync` remarks qualify their "never throws" statements with it (134-135, 179-182). That is the correct resolution: an accessor that throws is a programming error of the composition root, not a runtime sink failure, and guarding it would hide it. + +**Click boundary (main file 204-211).** `catch (Exception ex) { _ = TryInvokeSink(() => _logError(BuildToggleFailedMessage(engineName), ex), out _); }` The toggle fault still reaches the sink unchanged and is not rethrown; the #947 nested `try`/empty-`catch` is gone. `HandleToggleClickAsync_WhenToggleFaults_LogsErrorDoesNotThrowDoesNotInvalidate` and `..._WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport` both pass unchanged. + +**`CompletePrime` (Prime.cs 165-195).** `if (!_reportedPrimeFaults.ContainsKey(reportKey)) { if (TryInvokeSink(() => _logError(BuildPrimeFailedMessage(engineName), failure), out _)) { _reportedPrimeFaults[reportKey] = 0; } } _primeTasks.TryRemove(engineName, out _);` The record is the only statement of the branch taken on a normal sink return (190), so a throwing sink leaves the report owed (#948 placement preserved, now without a `try` in the method); `TryRemove` is the last statement (194), so report-then-clear (#942) and the per-key serialisation argument from the #948 review (the next prime for the key cannot register until this `TryRemove` has run) both hold. + +**Catch inventory.** Grep over the three files: two code `catch` clauses (main 208, 295); every other hit is a `catch` documentation mention. The executor's stripped census agrees (`catch(` 2, `catch(Exception)` 0, `TryInvokeSink(` 5 = one definition plus four call sites, `_logError(` 3, `_notifyUnavailable(` 1). + +### Ordering invariants after the split (#942 / #944 / #947 / #948) + +- Marker registered before the prime starts (#944): `StartPrimeIfNeeded` registers `marker.Task` at Prime.cs 63 inside the `_primeGate` lock and calls `StartObservedPrime` at 64; the explanatory comment travelled with the code (56-59). +- Prime fault reported before the marker is cleared (#942): `CompletePrime` 181-191 then 194, as above; the load-bearing comment travelled (176-179). +- A throwing sink leaves the fault kind unrecorded (#947 + #948): record only inside the `true` branch; the new guard test `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` pins it (two faults of one kind, sink throws on the first report only, `Errors` count 2, second prime distinct from the first). This closes a gap the #948 review noted (no test for the owed-report case). +- Repeat kind reported once (#948): `ContainsKey` guard at 181; the RepeatFaultSuppression partial is byte-identical to base and passes. +- Marker never faults or cancels: `StartObservedPrime` still completes it through `SetResult` in a `finally` (Prime.cs 95-102), and with every sink call contained the continuation has no remaining throw source, as the rewritten remarks (71-82) say. + +Evidence that nothing was weakened: `evidence/qa-gates/test-partials-unchanged.md` (four partials `exit=0`, Race `NON-DOC-CHANGES 0`, primary fixture's single removed line is the notification lambda replaced by the `OnNotify` form), and 43/43 at P1-T24 and in the 7388-test final run. + +### Documentation accuracy + +- `GetPrimeTask` (Prime.cs 10-26): summary opens "The registration marker for an engine key" and states "The marker is not the prime task itself: it is registered before the prime starts and is completed only after the prime outcome has been observed and, on a fault or cancellation, reported"; returns opens "The registered marker, or Task.CompletedTask when no marker is registered". Accurate against `_primeTasks` (main 64-73) and `StartObservedPrime`. #947 residual 2 closed. +- `HandleToggleClickAsync` summary (160-164) and remarks (170-183): "the only catch clause in this type that observes an engine fault. Every sink call on this path goes through TryInvokeSink, which holds the only other catch clause"; "never throws on either path, even when both sinks throw, provided the engines accessor honours its non-throwing precondition". Accurate. +- `StartObservedPrime` remarks (Prime.cs 71-82): "The two catch clauses in this type are the click boundary in HandleToggleClickAsync and the single sink guard in TryInvokeSink". Accurate. +- `CompletePrime` summary (131-137) and remarks (138-164): "a sink failure is contained by TryInvokeSink"; "recorded ... by the only statement of the branch taken when TryInvokeSink reports that the sink returned normally". Accurate against 183-191. +- Constructor parameter docs (87-108): accessor must not throw and why; `notifyUnavailable` guarded, failure reported once through `logError`; `logError` guarded, failure discarded. Accurate. +- Race partial remark (195-202): now says the second cancellation is a repeat kind under #948 and is not logged, and that the single-error assertion is still made before the re-prime so the test does not depend on the suppression rule. Accurate against the test body (217-236) and against `CompletePrime`. +- The nine retired phrases (for example "The prime task, or", "The other two are sink guards", "never throws, even when the sink throws") have zero hits across the three files. + +### Tests + +- **Framework and libraries (CUT1/CUT2):** MSTest attributes, the strict Moq mock from `Harness`, FluentAssertions with a because-reason on every assertion. `using` set minimal (System, Threading.Tasks, FluentAssertions, MSTest, Moq); nothing unused. +- **Harness change (primary fixture 414-418, 451-456):** the notification lambda now records then invokes an optional `OnNotify` hook, exactly the shape of the existing `OnLogError` hook, so a throwing hook both records the attempt and models a throwing sink. The hook is null by default, so every pre-existing test is unaffected (43/43). +- **Determinism:** the refusal path is synchronous; the guard test awaits the markers the continuation completes in a `finally`; no sleep, delay, timer, clock, parallelism attribute or temporary file (reviewer Grep over the seven partials: 0 hits). +- **Discrimination:** fail-before run with the fix provably absent (`TryInvokeSink(` 0 in the census taken immediately before the run): the three refusal-path tests fail with `notify sink failed` escaping, the stack naming the unguarded call; the guard test passes at base as its summary says it should (the #948 placement already held). Pass-after 43/43. The reason gate in the evidence quotes the discriminating message of each test. +- **Scenario completeness:** throwing notification sink alone; with the log sink also throwing; the owed-report case at the prime site. The null-key refusal case is the one missing negative input (CR-1). +- **Size:** SinkGuard 169 lines; primary fixture 481 (O-1). + +### Project files + +Two `` items in `TaskMaster.csproj` (467-468) directly after the main file's entry, one in `TaskMaster.Test.csproj` (364) after the RepeatFaultSuppression entry; same indentation as their neighbours in the caller's diff. Both files are outside CSharpier's scope by `.csharpierignore`; the two rebuilds compiled both projects (`CSC_OUT 2/2`), which is the compile-time proof the entries are well-formed, and the three class nodes in the final coverage document prove the partials were compiled and instrumented. + +### Evidence hygiene + +- 36 Markdown files in the feature folder, no other file type (Glob); executor hygiene sweeps at P2-T9 and P2-T20 report 0 account, machine and drive-path hits; the single absolute path in a trx failure message is transcribed as `REDACTED-PATH`. +- No raw trx or Cobertura document committed; `.gitignore` lines 146 (`*.trx`), 147 (`*cobertura*.xml`) and 150 (`coverage/*`) keep the retained raw documents out of git. Both coverage runs are committed as the JaCoCo package projection plus the one-line summary, and both test runs as trx-derived summaries, which is the CLAUDE.md Committed Test Evidence Format. +- Executor `Timestamp:` labels are clock-derived: the two Cobertura root epochs (07:39:58 and 08:11:55 -0400) sit two minutes before the 07-41 and 08-13 labels, and the head commit epoch (08:20:12 -0400) sits one minute after the last label (08-19). + +### Plan adherence + +Every task in `plan.2026-10-02T05-20.md` is checked (Grep for `- [ ]`: 0). Every evidence file the handoff names exists with `Timestamp:`, `Command:`, `EXIT_CODE:` and an output summary. The P1-T22 phrase census is recorded with its provenance (coordinator-run under a maintainer one-time bypass of `enforce-promotion-mcp-only.ps1`); this review re-derived every row by Grep and Read. + +## Observations (not findings) + +- O-1: the primary fixture `EngineToggleStateCoordinatorTests.cs` is at 481/500 lines. It is compliant, but the next harness member or test added there will need a `.Harness.cs` partial (the `Harness` and `LoggedError` types, lines 398-479, move cleanly). The CR-1 test should go in the SinkGuard partial for this reason. +- O-2: none of the three coordinator files carries `#nullable enable` (pre-existing state carried through the split). If the type ever opts in, `TryInvokeSink`'s parameter becomes `out Exception? sinkFailure` and the `(Exception)` cast in `CompletePrime` 172-174 needs re-reading. +- O-3: the canonical `artifacts/csharp/coverage.xml` path is absent in both checkouts; the committed projections and the local gitignored raw documents were used, per the standing ruling (recurring across #947, #948, #950, #968). +- O-4: the PR context artifact pair is absent in the review worktree; the session checkout's pair belongs to the #968 branch. Scope was verified from the caller name-status, the footprint evidence and the files on disk. +- O-5: `quality-tiers.yml` is absent at the repository root (pre-existing; already promoted by the #956 review). + +## Unrelated defects + +None found in the files read for this review. O-3 and O-5 are pre-existing repository-level conditions, not defects of this item, and both are already tracked. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T08-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T08-50.md new file mode 100644 index 000000000..a149b522d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T08-50.md @@ -0,0 +1,81 @@ +# Feature Audit: engine-toggle-coordinator-947-review-residuals (Issue #964) + +- Date: 2026-10-03 (review label `2026-10-03T08-50`; derived from the worktree reflog, see the policy audit) +- Work mode: `minor-audit` (`issue.md` line 12, the only `- Work Mode:` line). AC source: the `## Acceptance Criteria` section of `issue.md` only (heading at line 23; eight checkbox lines 27-34; next heading `## Environment` at line 36). `spec.md` and `user-story.md` are intentionally absent (Glob of the feature folder: none). +- Branch: `bug/engine-toggle-coordinator-947-review-residuals-964` + +## Scope and Baseline + +| Item | Value | Source | +|---|---|---| +| Base | origin/main `993fdd01566dee82e5f37acb761a600feaaa1454`, merged into the item at `981abef77657adcc90d7c116a6b4c6500b79ea29` (reflog entry `merge origin/main`, epoch 1790993935) | caller prompt; worktree reflog; `evidence/qa-gates/footprint-scope.md` | +| Pre-change text anchor | `94287369908cc920b21b0e3256314f988ad7d2f5` (executor Phase 0 anchor); the footprint negative control shows no TaskMaster or TaskMaster.Test path changed between that anchor and the merge commit | `evidence/baseline/scope-and-anchor.md`; `evidence/qa-gates/footprint-scope.md` negative control 2 (empty) | +| Head commit | `2fed92d2f4807653b32ef0236b6f1d82a6084ed5` (`docs(964): record hygiene sweeps, AC1-AC8 check-offs and reduced-audit handoff (P2-T9 to P2-T20)`) | worktree reflog, last entry | +| Intermediate commits after the merge | `98934d356`, `0561003e9`, `08511aa6e` (Phase 0 evidence), `1b0304f88` (split), `9de37caf1` (tests, fail-before), `ca215e068` (fix), `fc1624489`, `f179a4426` (Phase 1 and 2 evidence) | reflog; `evidence/other/reduced-audit-handoff.md` | +| Code footprint (origin/main..HEAD) | `M TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (496 -> 302 lines), `A TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (197), `A TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (86), `M TaskMaster/TaskMaster.csproj` (+2), `M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (470 -> 481), `A TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (169), `M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (remark only), `M TaskMaster.Test/TaskMaster.Test.csproj` (+1) | caller name-status and verbatim diff; `evidence/qa-gates/footprint-scope.md`; files re-read | +| Non-code footprint | feature folder only (issue.md check-offs, plan, 34 evidence files, preflight clearance) and `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md` | same | +| Baseline behaviour (defect) | `HandleToggleClickAsync` called `_notifyUnavailable(...)` unguarded on the refusal path (pre-fix line 178), so a throwing notification sink escaped into the `async void` Office handler; `GetPrimeTask` documentation opened "The in-flight ... prime"; the file was 496/500 lines | `evidence/regression-testing/refusal-path-fail-before.md` (stack naming line 178; stripped census `_notifyUnavailable(` 1 unguarded, `TryInvokeSink(` 0); `evidence/regression-testing/split-census.md` BASE-LINES 496 | +| Baseline tests | fixture 39/39; suite 7384/7384 | `evidence/baseline/coordinator-tests-baseline.md`; `evidence/baseline/coverage-baseline.md` | +| Baseline coverage | first-party 85.96% lines / 80.10% branches; coordinator 177/177 lines, 39/40 branches (one class node) | `evidence/baseline/coverage-baseline.md`; root and class node of `coverage/baseline-964.cobertura.xml` re-read | +| Post-change tests | fixture 43/43; suite 7388/7388 | `evidence/regression-testing/refusal-path-pass-after.md`; `evidence/qa-gates/coverage-final.md` | +| Post-change coverage | first-party 85.96% lines / 80.10% branches; coordinator 203/203 lines (89 + 72 + 42), 43/44 branches (three class nodes); new methods 10/10 and 8/8; every rewritten line hits=1 | `evidence/qa-gates/coverage-final.md`; `evidence/qa-gates/coverage-comparison.md`; root and class nodes of `coverage/final-964.cobertura.xml` re-read | +| PR context artifacts | absent from the review worktree; the session checkout's pair belongs to the #968 branch; not regenerable without a shell. Scope derived from the three agreeing sources above. | Glob; Read | + +## Acceptance Criteria Inventory + +Source: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`, section `## Acceptance Criteria`. All eight are checkbox items and all eight read `- [x]` at head (executor check-offs at P2-T10 to P2-T17, one flip per task, criterion text unmodified; `evidence/other/ac-status-summary.md`). + +| ID | Criterion (verbatim opening) | Checkbox at head | +|---|---|---| +| AC1 | (refusal-path notification guard, regression-first): with the engines accessor returning null and a `notifyUnavailable` sink that throws, `EngineToggleStateCoordinator.HandleToggleClickAsync` completes without throwing. A new MSTest regression test proves this; it is recorded failing against the unmodified coordinator ... and passing after the fix. | `[x]` | +| AC2 | (notification failure is reported, not lost): in the AC1 scenario the notification sink is attempted exactly once, the sink's exception is delivered exactly once to `logError` (the same exception instance), no engine member is invoked, and no control is invalidated. When `logError` also throws in that scenario, `HandleToggleClickAsync` still completes without throwing. Both behaviours are asserted by named MSTest tests. | `[x]` | +| AC3 | (one shared sink guard): all three sink call sites ... route through a single private guard helper; no other `catch` clause that discards a sink exception remains in the coordinator's source files. Verified by reading the split source and by the existing #947 tests in `EngineToggleStateCoordinatorTests.ThrowingSink.cs` passing unchanged. | `[x]` | +| AC4 | (prime-marker ordering invariants preserved across the split): ... the marker is registered before the prime starts; a prime fault is reported before the marker is cleared; a sink that throws leaves the fault kind unrecorded (report still owed); a repeated fault kind for the same engine is reported once. Verified by every existing test in the `EngineToggleStateCoordinatorTests` partials ... passing with no assertion weakened or removed. | `[x]` | +| AC5 | (`GetPrimeTask` documentation accuracy): the `GetPrimeTask` `` and `` describe the registration marker (completed only after the prime outcome has been observed and reported), not "the prime task" or "the in-flight prime". | `[x]` | +| AC6 | (file-size split): `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` is split into cohesive partial-class files of the same `internal sealed partial class EngineToggleStateCoordinator`, each file at or below 450 lines, each new file registered as a `Compile` item in `TaskMaster/TaskMaster.csproj`; every touched test file stays at or below 500 lines. | `[x]` | +| AC7 | (comment drift in touched files): every comment that counts or locates the coordinator's `catch` clauses ... and the `HandleToggleClickAsync` "never throws" remark match the post-change code, and the constructor's `notifyUnavailable` and `enginesAccessor` parameter docs state the guarded behaviour and the accessor's non-throwing precondition. | `[x]` | +| AC8 | (toolchain and coverage): the CLAUDE.md C# toolchain passes in one clean pass (csharpier check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild` without `/p:Nullable=enable`, `Invoke-MSTestWithCoverage.ps1`), with no new failing test relative to baseline and the coordinator's line coverage not lower than its baseline figure. | `[x]` | + +## Acceptance Criteria Evaluation + +| ID | Status | Evidence and reasoning | +|---|---|---| +| AC1 | PASS | Code: `EngineToggleStateCoordinator.cs` 186-202, the notification call is the argument of `TryInvokeSink` (189-193), whose `catch (Exception ex)` (295-299) assigns the exception and returns `false`; nothing on the path rethrows. Test: `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` (`SinkGuard.cs` 31-43) arranges `EnginesAvailable = false` and a throwing `OnNotify`, asserts `NotThrowAsync`. Fail-before: `evidence/regression-testing/refusal-path-fail-before.md`, `EXIT_CODE: 1` with `ExpectedExitCode: 1`, the stripped census immediately before the run shows `TryInvokeSink(` 0 (fix absent), `FAILED` with MESSAGE `Did not expect any exception because a throwing notification sink must not escape the refusal path, but found System.InvalidOperationException: notify sink failed`, stack naming the pre-fix line 178. Pass-after: `refusal-path-pass-after.md` `Passed`; final run FAILED-FQN-COUNT 0. | +| AC2 | PASS | Test one, `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing` (52-79): `Notifications.Should().ContainSingle()` (attempted once, recorded before the hook throws, fixture 414-418), `Errors.Should().ContainSingle()` and `Errors[0].Exception.Should().BeSameAs(notifyFailure)` (delivered once, same instance), `Errors[0].Message.Should().Contain(SpamEngine)`, `Engines.VerifyNoOtherCalls()` on the strict mock (no engine member), `Invalidations.Should().BeEmpty()` (no control invalidated). Test two, `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` (88-112): both hooks throw; `NotThrowAsync`, plus the single notification, single log attempt and same-instance assertions. Code: the forwarding call at 195-198 is itself guarded and its result discarded. Both tests Failed at P1-T14 for their recorded reasons (`threw exception` / `the refusal path contains a failure of both sinks`, each with `notify sink failed`) and Passed at P1-T24 and P2-T5. | +| AC3 | PASS | Call sites: `_notifyUnavailable(` once (main 190, inside a `TryInvokeSink` lambda); `_logError(` three times (main 196 and 210, Prime.cs 185), each inside a `TryInvokeSink` lambda; `TryInvokeSink` is `private static` (main 287). Catch clauses in the three source files (reviewer Grep): main 208 (click boundary, body routes through the guard, discards nothing itself) and main 295 (the guard); Prime.cs and Messages.cs contain none (every other hit is a `catch` doc mention). The #947 comment-only `catch (Exception)` blocks are gone (census `catch(Exception)` 0). `ThrowingSink.cs` byte-identical to the pre-change anchor (`evidence/qa-gates/test-partials-unchanged.md` `exit=0`); its four tests Passed at P1-T24 (listed by name) and in the final run. | +| AC4 | PASS | Registration before start: Prime.cs 63 (`_primeTasks[engineName] = marker.Task;`) precedes 64 (`StartObservedPrime(...)`) inside the `_primeGate` lock. Report before clear: Prime.cs 183-188 (`TryInvokeSink(() => _logError(...), out _)`) precedes 194 (`_primeTasks.TryRemove`), the last statement. Throwing sink leaves the kind unrecorded: the record at 190 is the only statement of the `if (TryInvokeSink(...))` branch; the new guard test `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` (126-165) asserts `Errors` count 2 and a distinct second prime handle, Passed before and after. Repeat kind reported once: `ContainsKey` guard at 181; `GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly` and `..._WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly` Passed. Nothing weakened: PrimeFaultOrdering, PrimeRegistration, ThrowingSink and RepeatFaultSuppression byte-identical to the anchor; Race changed by four remark lines only (`NON-DOC-CHANGES 0`, confirmed against the caller's diff); the primary fixture's only removed line is the notification lambda, replaced by the record-then-hook form. All eleven invariant-named tests Passed at P1-T8 (after the pure move, 39/39), P1-T14 (fix absent) and P1-T24 (43/43). | +| AC5 | PASS | Prime.cs 10-15 summary: "The registration marker for an engine key, exposed so tests can await the outcome of its prime deterministically instead of polling or sleeping. The marker is not the prime task itself: it is registered before the prime starts and is completed only after the prime outcome has been observed and, on a fault or cancellation, reported." Lines 17-26 returns: "The registered marker, or Task.CompletedTask when no marker is registered for the key. The marker never faults or cancels ... completed in a finally after that observation ...". Reviewer Grep for `The prime task, or`, `The in-flight` and `most recently completed` over the three files: 0. Matches `_primeTasks` (main 64-73) and `StartObservedPrime` (Prime.cs 89-107). | +| AC6 | PASS | Three files, each `internal sealed partial class EngineToggleStateCoordinator` in `namespace TaskMaster` (main 43, Prime.cs 8, Messages.cs 7); reviewer Read line counts 302 / 197 / 86 (each at or below 450; agrees with `evidence/qa-gates/file-line-counts.md`); `TaskMaster.csproj` 466-468 registers all three (two added); touched test files 481 (primary), 277 (Race), 169 (SinkGuard), each at or below 500; `TaskMaster.Test.csproj` 364 registers the new partial. Build: analyzer and TreatWarningsAsErrors rebuilds exit 0 with `CSC_OUT_TASKMASTER 2` and `CSC_OUT_TASKMASTER_TEST 2`; the final coverage document carries one `` node per production file. Cohesion: contract and click boundary / prime lifecycle / messages (code review, section "The split"). | +| AC7 | PASS | Each named comment read against the code: `HandleToggleClickAsync` summary (main 160-164: "the only catch clause in this type that observes an engine fault. Every sink call on this path goes through TryInvokeSink, which holds the only other catch clause") and remarks (170-183: "never throws on either path, even when both sinks throw, provided the engines accessor honours its non-throwing precondition"); `StartObservedPrime` remarks (Prime.cs 71-82: "The two catch clauses in this type are the click boundary ... and the single sink guard in TryInvokeSink"); `CompletePrime` summary (131-137: "a sink failure is contained by TryInvokeSink") and remarks (157-162: "recorded ... by the only statement of the branch taken when TryInvokeSink reports that the sink returned normally"); constructor docs for `enginesAccessor` (87-93: "must not throw ... read outside any guard by GetPressed and by the refusal check of HandleToggleClickAsync") and `notifyUnavailable` (98-103: "The call is guarded (issue #964): an exception it throws is reported once through logError and is not rethrown"). Two catch clauses exist (main 208, 295), so every count is correct. The nine retired phrases have 0 hits (reviewer Grep). The executor's 24-row phrase census (coordinator-run, provenance recorded) agrees row for row. | +| AC8 | PASS | `evidence/qa-gates/toolchain-final-pass.md`: pass 1 clean. Step 1: `csharpier format .` rewrote no Write Set file, `csharpier check .` exit 0, 1640 files. Step 2: analyzer `/t:Rebuild` with `EnableNETAnalyzers` and `EnforceCodeStyleInBuild`, exit 0, 0 errors, 0 warnings, `CSC_OUT 2/2`. Step 3: `/t:Rebuild` with `TreatWarningsAsErrors=true` and no `/p:Nullable=enable`, exit 0, 0 errors, 0 warnings. Step 4: `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`, runner exit 0, 7388/7388 (baseline 7384/7384, the four additions are the new tests), `FINAL-FAILED-FQN-COUNT 0`, LINE-FLOOR and BRANCH-FLOOR MET (85.96% / 80.10%). Coordinator line coverage 100% at both stages (177/177 then 203/203), re-read at the class nodes of both raw documents. | + +Totals: 8 PASS, 0 PARTIAL, 0 FAIL, 0 UNVERIFIED. + +## Acceptance Criteria Check-off + +All eight criteria were already checked `[x]` by the executor (P2-T10 to P2-T17, one flip per task, criterion text unmodified; `evidence/other/ac-status-summary.md` records total 8, checked off 8, remaining 0; `evidence/other/reduced-audit-handoff.md` P2-T19 records eight check-off edits, 8 lines changed). This review evaluated every criterion PASS, so no checkbox was changed and none was unchecked. Newly checked off by this review: none. + +### Acceptance Criteria Status +- Source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md +- Total AC items: 8 +- Checked off (delivered): 8 +- Remaining (unchecked): 0 +- Items remaining: none + +## Summary + +**Verdict: PASS.** 8/8 acceptance criteria met on evidence; 0 Blocking findings across the three review artifacts; 4 Non-blocking findings in the code review (CR-1 Minor: the pre-existing untested null-key arm of `RenderEngineName`, now isolated in the Messages partial; CR-2 to CR-4 Informational) and 5 observations (O-1 to O-5). Remediation inputs: not produced. + +Independent verification performed by this review (no command executed): the three production files, the primary fixture, the SinkGuard and ThrowingSink partials and the changed Race remark read in full in the item worktree; the caller's verbatim diff read in full; `RibbonCommandBoundary.ReportFailure` and `SafeLog` read to confirm the precedent the new remarks cite; both raw Cobertura documents read at the root and at every coordinator `` node; the worktree reflog read to confirm head, merge base and commit times and to cross-check the executor's labels; the feature folder enumerated for file types; the catch-clause, sink-call-site, banned-API and retired-phrase censuses re-derived by Grep. + +### Related-defect dispositions (for the coordinator) + +| ID | Item | Related to this item | Disposition requested | +|---|---|---|---| +| CR-1 | `RenderEngineName` null-or-empty arm untested; Messages partial node 50% branches, type 97.73% | Yes (same files, same component) | Non-blocking; one refusal-path test with a null engine key (and optionally `""`), placed in the SinkGuard partial | +| CR-4 | Both-sinks-throw test lacks the two symmetry assertions its sibling carries | Yes (touched file) | Optional | +| O-1 | Primary fixture at 481/500 lines | Yes (touched file), compliant | No action in this item; move `Harness` and `LoggedError` to a `.Harness.cs` partial at the next addition | + +### Unrelated defects + +None found. Pre-existing repository-level conditions (canonical C# coverage artifact path absent; `quality-tiers.yml` absent) are already tracked and are not defects of this item. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T08-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T08-50.md new file mode 100644 index 000000000..a7e60877d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T08-50.md @@ -0,0 +1,276 @@ +# Policy Audit: engine-toggle-coordinator-947-review-residuals (Issue #964) + +- Timestamp: 2026-10-03T08-50 +- Branch: bug/engine-toggle-coordinator-947-review-residuals-964 +- Head: 2fed92d2f4807653b32ef0236b6f1d82a6084ed5 (worktree reflog, last entry: `docs(964): record hygiene sweeps, AC1-AC8 check-offs and reduced-audit handoff (P2-T9 to P2-T20)`) +- Base: origin/main 993fdd01566dee82e5f37acb761a600feaaa1454, merged into the item at 981abef77657adcc90d7c116a6b4c6500b79ea29 (reflog entry `merge origin/main`, epoch 1790993935). origin/main is an ancestor of the head, so the item change set is the two-dot diff origin/main..HEAD, which the caller supplied as a name-status listing and which agrees with evidence/qa-gates/footprint-scope.md (31 paths at P2-T8 plus the later evidence-only commits) and with the files on disk. The executor's Phase 0 anchor for the pre-change text was 94287369908cc920b21b0e3256314f988ad7d2f5 (evidence/baseline/scope-and-anchor.md); the negative control in footprint-scope.md shows that no path under TaskMaster or TaskMaster.Test changed between that anchor and the merge commit, so the executor's base census is the pre-change text of every code file in scope. +- Work mode: minor-audit (issue.md line 12); acceptance-criteria source: the `## Acceptance Criteria` section of issue.md only (AC1 to AC8, lines 27 to 34) +- Reviewer: feature-review, no-Bash mode (caller directive). Every check was performed with Read, Grep and Glob against the item worktree, the committed evidence under evidence/, the caller-supplied verbatim diff, the gitignored raw Cobertura documents at their local paths, and the worktree reflog as head reference and clock. Where a check would need a shell it is recorded as such with the reason. +- Timestamp derivation: the label above is monotone after the head commit's reflog epoch 1791030012 (2026-10-03T12:20:12Z, 08:20:12 at the recorded -0400 offset) and after every label in the feature folder (latest 2026-10-03T08-19). No shell clock was readable in this session. The executor's labels were cross-checked against two independent epochs: the baseline Cobertura root `timestamp="1791027598"` (07:39:58 -0400) sits two minutes before the 07-41 label of evidence/baseline/coverage-baseline.md, and the final Cobertura root `timestamp="1791029515"` (08:11:55 -0400) sits two minutes before the 08-13 label of evidence/qa-gates/coverage-final.md, so the evidence labels are clock-derived. + +## Executive Summary + +Overall verdict: PASS. 0 Blocking findings. 0 findings of class autonomous, external_dependency, policy_hold, awaiting_ci or human_decision_required that block the pull request. 1 Non-blocking Minor finding (CR-1, a pre-existing untested branch arm that the split now isolates in a new file) and 3 Informational findings, detailed in code-review.2026-10-03T08-50.md. AC1 to AC8 verified PASS against code and evidence; all eight were already checked off by the executor and none is unchecked by this review. Remediation inputs: not produced. + +| Area | Verdict | Evidence summary | +|---|---|---| +| General Unit Test Policy | PASS | Four new MSTest tests driven by TaskCompletionSource and the coordinator's own prime marker; no Thread.Sleep, Task.Delay, wall-clock read, temporary file or parallelism attribute (reviewer Grep over the seven fixture partials: 0 hits); 7384/7384 at baseline, 7388/7388 after the change | +| General Code Change Policy | PASS | Failing regression test first (three refusal-path tests Failed at P1-T14 with the fix provably absent, Passed at P1-T24); the split is a pure move (ordinal multiset census: eight structural difference lines only); the fix is one private guard helper plus three call-site rewrites; every file at or under 500 lines (production 302/197/86, largest test partial 481); toolchain single pass | +| C# Code Change Policy | PASS | csharpier check exit 0 (1640 files); analyzer /t:Rebuild 0 errors 0 warnings, CSC_OUT 2/2; TreatWarningsAsErrors /t:Rebuild 0 errors 0 warnings, CSC_OUT 2/2, no /p:Nullable=enable; XML docs on every new and changed member | +| C# Unit Test Policy | PASS | MSTest, Moq, FluentAssertions throughout; first-party lines 85.96%, branches 80.10% (floors 80/75 per CLAUDE.md, 85/75 per .claude/rules, both met); coordinator type 203/203 lines (baseline 177/177), 43/44 branches (baseline 39/40) | +| Coverage (C#) | PASS | Repo-wide 85.96% lines / 80.10% branches at both stages; new methods TryInvokeSink 10/10 and BuildNotifyFailedMessage 8/8 lines; every new refusal-path line hits=1; the four added branches all covered; the single uncovered arm (RenderEngineName null-or-empty) is pre-existing and recorded as CR-1 | +| Evidence hygiene | PASS | 0 host paths, 0 account names and 0 raw trx or coverage documents in the committed feature folder (executor CMD-HYGIENE at P2-T9 and P2-T20: 36 files, all counters 0; reviewer Glob: 36 Markdown files, no other file type) | +| Committed Test Evidence Format | PASS | Both coverage runs committed as the package-level JaCoCo projection plus the one-line first-party summary; the test runs committed as trx-derived summaries; the raw documents stay under the gitignored coverage/ directory (.gitignore lines 146, 147, 150) | + +## Rejected Scope Narrowing + +No scope narrowing was detected in the caller prompt. The following caller statements were evaluated and accepted as factual, as tooling constraints, or as the work-mode rule rather than as narrowing: + +- "Do NOT use the Bash tool at all." A tooling constraint, not a scope constraint. The audit scope remains the full branch diff against origin/main; every changed code file was read in full from the worktree, the caller's verbatim diff was read in full, and the executor's pre-change census was used as the before-text where a comparison was needed. +- "reduced (minor-audit) review" and "AC source: FEATURE/issue.md `## Acceptance Criteria` only". This is the work-mode rule from issue.md line 12 (`- Work Mode: minor-audit`) applied per the acceptance-criteria-tracking skill; it governs the AC source, not the policy scope. Every policy was evaluated over the full diff. +- "Code diff versus base (C# production and test only)" and "Full name-status versus base: 8 code files ... plus only additions under the feature folder ... and docs/features/potential/promoted/...". Confirmed against evidence/qa-gates/footprint-scope.md and the files on disk. Zero PowerShell, TypeScript or Python files changed; no language was declared not applicable by the caller. +- "check off only PASS items (they are already checked; uncheck nothing, but report any you judge not PASS as a finding)". Consistent with the skill's reviewer check-off protocol; this review evaluated every criterion independently. + +## Evidence Location Compliance + +- Branch diff scan for files under artifacts/baselines/, artifacts/qa/, artifacts/evidence/ or artifacts/coverage/: none. The name-status origin/main..HEAD lists the eight code paths, one promoted record under docs/features/potential/promoted/ and paths under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/ only. +- All executor evidence lives under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/{baseline,regression-testing,qa-gates,other}/ (13 + 5 + 12 + 4 Markdown files; Glob listing in this review, 36 files including issue.md and the plan). +- validate_evidence_locations.py --root .: not run (Bash was forbidden for this review; a Glob for the script over the worktree returned nothing, so it is not present in this checkout). The manual scan above substitutes; no violation observed. +- EVIDENCE_LOCATION_OVERRIDE_REJECTED: none required; the caller supplied no non-canonical evidence path. +- PR context artifacts (artifacts/pr_context.summary.txt, artifacts/pr_context.appendix.txt): absent in the review worktree (Glob of the exact path returned nothing). The session checkout carries a pair generated 2026-10-03 07:59:49 UTC for head 78e24a68c of the #968 branch, which is a different item and is therefore not evidence for this review. Regeneration was not possible without a shell or the collection tool. Scope was derived from the caller-supplied name-status and verbatim diff, the committed footprint evidence and the files on disk; the scope is verified by three agreeing sources even though the artifact pair itself could not be produced. +- Raw coverage documents: coverage/baseline-964.cobertura.xml and coverage/final-964.cobertura.xml exist locally in the worktree (gitignored, not committed). Root elements read for this review: baseline line-rate 0.859601, branch-rate 0.801031, lines 56609/65855, branches 13680/17078; final line-rate 0.859565, branch-rate 0.801019, lines 56629/65881, branches 13683/17082. The canonical path artifacts/csharp/coverage.xml is absent in both checkouts; the committed JaCoCo package projection plus one-line summary are the forms CLAUDE.md "Committed Test Evidence Format" requires, and the standing ruling treats executor-committed feature-folder coverage evidence as the present artifact. + +## 1. General Unit Test Policy Compliance + +### 1.1 Core principles + +| Principle | Verdict | Evidence | +|---|---|---| +| Independence | PASS | Every new test constructs its own Harness (SinkGuard.cs lines 34, 55, 91, 129); no static state is read or written; the fixture's existing `[TestClass]` runs under the repository runsettings without any parallelism attribute (Grep for DoNotParallelize over the seven partials: 0) | +| Isolation | PASS | Each refusal-path test targets one guarantee (does not throw; reports once and invokes nothing; survives both sinks throwing); the guard test targets the record-after-sink placement alone; the fail-before run names exactly the three refusal-path tests and no other (evidence/regression-testing/refusal-path-fail-before.md) | +| Fast execution | PASS | Every asynchronous outcome is a TaskCompletionSource completed by the test; no bounded wait, timer or pump; the fixture run of 43 tests completed inside the P1-T24 vstest invocation with no Sequence file | +| Determinism | PASS | The refusal path is synchronous up to its `return`, so `await HandleToggleClickAsync` observes the complete outcome; the guard test resynchronises through `await firstPrime` and `await secondPrime`, both markers completed by `SetResult` in a `finally` (Prime.cs lines 95-102); no clock, random value or filesystem is read (reviewer Grep over the seven partials for Thread.Sleep, Task.Delay, DateTime.Now, DateTime.UtcNow, Stopwatch, Path.GetTemp, Environment.TickCount, .Wait(), .Result, SpinWait: 0 hits) | +| Readability | PASS | Descriptive names stating scenario and expected outcome; XML `` on every new test stating what fails without the fix; Arrange / Act / Assert markers; a because-reason on every assertion | + +### 1.2 Coverage + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|---|---|---|---|---|---|---| +| C# | 6 | 7388 | 7388 passed, 0 failed | 85.96% lines / 80.10% branches | 85.96% lines / 80.10% branches | 100% lines / 100% branches | +| TypeScript | 0 | N/A | N/A | N/A | N/A | N/A | +| PowerShell | 0 | N/A | N/A | N/A | N/A | N/A | +| Python | 0 | N/A | N/A | N/A | N/A | N/A | + +Files Changed counts .cs files only (six: three production, of which two added, and three test, of which one added); the two project files TaskMaster/TaskMaster.csproj and TaskMaster.Test/TaskMaster.Test.csproj are the remaining code paths. New Code Coverage is measured over the executable lines and branches this item added: TryInvokeSink (10 of 10 lines, EngineToggleStateCoordinator.cs 288-300), BuildNotifyFailedMessage (8 of 8 lines, Messages.cs 51-58), the rewritten refusal path and click-boundary catch body (EngineToggleStateCoordinator.cs 186-201 and 210, every line hits=1 in evidence/qa-gates/coverage-final.md METHOD-LINE rows), and the four branches added to the type (COORD-BRANCHES 39/40 at baseline to 43/44 after the change: +4 valid, +4 covered). + +Coverage source statement: the figures above are read from the committed projections and summaries (evidence/baseline/coverage-baseline.md at P0-T14; evidence/qa-gates/coverage-final.md at P2-T5; evidence/qa-gates/coverage-comparison.md at P2-T6), each carrying the first-party summary line, the root counters, the package-level JaCoCo projection and the coordinator class-node rows, and were cross-checked by this review against the root elements and the three coordinator `` nodes of the local raw documents coverage/baseline-964.cobertura.xml (one node, line-rate 1, branch-rate 0.975) and coverage/final-964.cobertura.xml (three nodes: EngineToggleStateCoordinator.cs line-rate 1 branch-rate 1; Prime.cs line-rate 1 branch-rate 1; Messages.cs line-rate 1 branch-rate 0.5). Both runs used the same route, `Invoke-MSTestWithCoverage.ps1`, over nine test assemblies, so they are comparable. + +Verdict lines: + +- C# coverage verdict: PASS (repo-wide first-party lines 85.96% and branches 80.10% from the committed post-change projection and the raw root element; above the CLAUDE.md floors of 80% lines and 75% branches and above the 85% / 75% floors in .claude/rules; equal to the baseline 85.96% / 80.10% at two decimals). +- C# new-code coverage: PASS. 100% of the added executable lines and 100% of the added branches are covered (18 new method lines, the 13 rewritten refusal-path and catch-body lines, and 4 added branches, all hits=1 or covered in the final document). +- C# changed-production-file coverage: PASS on the no-regression limb and on the file-level line floor. EngineToggleStateCoordinator.cs 89/89 lines (was 177/177 as the single file), Prime.cs 72/72, Messages.cs 42/42; the type's branch count rose from 39/40 to 43/44 and the single uncovered arm is the same pre-existing arm at both stages (RenderEngineName's null-or-empty branch, now the only branch in Messages.cs, whose node therefore reads branch-rate 0.5). Nothing regressed; the pre-existing arm is recorded as non-blocking finding CR-1 with a one-test remedy. +- C# package-level corroboration: the TaskMaster package counters moved from 2477 to 2503 covered lines with 802 missed at both stages (+26 covered of +26 added) and from 519 to 523 covered branches with 211 missed at both stages (+4 of +4). The UtilitiesCS package moved by -6 covered lines and -1 covered branch with no UtilitiesCS file changed on this branch; this is run-to-run variance in code outside the Write Set (the executor recorded the same observation) and is within the band observed on earlier reviews. +- PowerShell coverage gate: PASS by vacuity (zero PowerShell files changed on this branch, so the changed-line no-regression requirement has no line to evaluate; no PoshQC format, analyze or test gate was owed or run; artifacts/pester/powershell-coverage.xml was not consulted). +- TypeScript and Python: zero files changed on this branch; no verdict is owed. + +### Coverage Evidence Checklist + +- C# baseline coverage artifact: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md` (committed one-line first-party summary, root counters, JaCoCo package projection and coordinator class-node rows; raw document coverage/baseline-964.cobertura.xml present locally, gitignored; canonical artifacts/csharp/coverage.xml absent in the worktree) +- C# post-change coverage artifact: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/coverage-final.md` with `evidence/qa-gates/coverage-comparison.md` (same forms; raw document coverage/final-964.cobertura.xml present locally, gitignored; canonical artifacts/csharp/coverage.xml absent in the worktree) +- TypeScript baseline coverage artifact: none consulted (zero TypeScript files changed on this branch) +- TypeScript post-change coverage artifact: none consulted (zero TypeScript files changed on this branch) +- PowerShell baseline coverage artifact: none consulted (zero PowerShell files changed on this branch) +- PowerShell post-change coverage artifact: none consulted (zero PowerShell files changed on this branch) +- Python baseline coverage artifact: none consulted (zero Python files changed on this branch) +- Python post-change coverage artifact: none consulted (zero Python files changed on this branch) +- Per-language comparison summary: the per-language comparison block of this document + +### 1.2.1 Per-Language Coverage Comparison + +- C#: Baseline: 85.96% lines (56609/65855) / 80.10% branches (13680/17078). Post-change: 85.96% lines (56629/65881) / 80.10% branches (13683/17082). Change: 0.00% lines / 0.00% branches at two decimals (+20 covered of +26 valid lines, +3 covered of +4 valid branches; at full precision line-rate 0.859601 to 0.859565 and branch-rate 0.801031 to 0.801019, the whole of that movement being the UtilitiesCS run-to-run variance described above while the TaskMaster package gained 26 covered of 26 added lines and 4 covered of 4 added branches). New/changed-code coverage: 100%. Disposition: PASS. Evidence: evidence/baseline/coverage-baseline.md, evidence/qa-gates/coverage-final.md, evidence/qa-gates/coverage-comparison.md, root elements and coordinator class nodes of coverage/baseline-964.cobertura.xml and coverage/final-964.cobertura.xml. +- TypeScript: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero TypeScript files changed on this branch. +- PowerShell: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero PowerShell files changed on this branch. +- Python: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero Python files changed on this branch. + +### 1.2.2 Coverage Artifact State + +| Language | Artifact consulted | State | Disposition | +|---|---|---|---| +| C# | Committed projections, summaries and class-node rows under evidence/baseline and evidence/qa-gates; raw Cobertura root elements and class nodes read locally | Present; canonical artifacts/csharp/coverage.xml absent in the worktree (recorded as observation O-3, recurring) | PASS | +| TypeScript | none | zero files changed | no verdict owed | +| PowerShell | none | zero files changed | no verdict owed | +| Python | none | zero files changed | no verdict owed | + +Coverage exclusion policy check (.claude/rules/general-unit-test.md): the branch adds no coverage-config exclude entry and no ExcludeFromCodeCoverage attribute (Grep over the three production files: the only occurrence is the pre-existing class remark stating the type is deliberately NOT excluded, EngineToggleStateCoordinator.cs line 34). Not Blocking. + +### 1.3 Scenario completeness + +| Scenario | Verdict | Evidence | +|---|---|---| +| Positive flows | PASS | Refused click with a healthy notification sink still notifies once and invokes nothing (pre-existing HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing, Passed); healthy prime and toggle paths unchanged and Passed (43/43) | +| Negative flows | PASS | Throwing notification sink (AC1); throwing notification sink reported once through the log sink with the same exception instance, no engine member invoked, no invalidation (AC2); both sinks throwing (AC2); throwing log sink on a faulted prime leaves the report owed so the repeat is reported (SinkGuard guard test) | +| Edge cases | PASS | The record-placement guard test exercises the sink-throws-on-first-report, returns-on-second sequence through two primes of one fault kind; the pre-existing #948 repeat-suppression and #947 throwing-sink partials are byte-identical to base and Passed | +| Error handling | PASS | Every sink call site contained by TryInvokeSink; the click boundary still reports the toggle fault unchanged (HandleToggleClickAsync_WhenToggleFaults_LogsErrorDoesNotThrowDoesNotInvalidate, Passed); ExecuteToggleAsync still propagates (ExecuteToggleAsync_WhenToggleFaults_PropagatesUnchanged, Passed) | +| Concurrency | PASS | The split preserves the lock-scoped registration and the keyed TryRemove as the last statement of CompletePrime (Prime.cs 49-65 and 194); the Race partial is unchanged except one remark; all eleven invariant-named tests Passed before and after the fix | +| State transitions | PASS | Marker registered -> prime -> report attempted -> (recorded only on a normal sink return) -> marker cleared; observed by the guard test (secondPrime distinct from firstPrime, Errors count 2) and by the unchanged PrimeFaultOrdering and PrimeRegistration partials | + +### 1.4 Arrange-Act-Assert + +PASS. Every new test carries Arrange / Act / Assert markers (SinkGuard.cs lines 33, 37, 40, 54, 59, 62, 90, 96, 99, 128, 148, 156); every assertion carries a because-reason string; the `Func act` under Act with the awaited `NotThrowAsync` under Assert mirrors the existing fixture test at EngineToggleStateCoordinatorTests.cs lines 342-346. + +### 1.5 External dependencies and temporary files + +PASS. No Outlook COM, file system, network or process is touched by any changed test; the engines are a strict Moq mock and the three sinks are recording delegates. Reviewer Grep over the seven fixture partials for Path.GetTemp, System.IO member use in the changed files, Thread.Sleep and Task.Delay: 0 hits in the changed files (the unchanged RepeatFaultSuppression partial imports System.IO for an IOException instance used as a second fault kind, which is not I/O). + +### 1.6 Test file location + +PASS (repository convention). The new partial sits at TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs beside the six existing partials of the same fixture, mirroring the per-project *.Test layout used for every C# project in this repository; no test file was colocated with production source. + +### 1.7 Determinism infrastructure + +PASS. No time is consumed by any new test; no randomness is used; banned APIs in test code: Thread.Sleep 0, Task.Delay 0, Date/clock reads 0 over the seven fixture partials (reviewer Grep). The coordinator type reads no clock. + +## 2. General Code Change Policy Compliance + +| Item | Verdict | Evidence | +|---|---|---| +| Before making changes (plan, AC source) | PASS | issue.md carries the explicit `## Acceptance Criteria` section (AC1 to AC8) and the minor-audit marker; plan.2026-10-02T05-20.md exists with every task checked (reviewer Grep for `- [ ]`: 0); preflight clearance recorded at evidence/other/preflight-clearance.2026-10-02T07-50.md | +| Bugfix workflow step 1 (failing regression test first) | PASS | evidence/regression-testing/refusal-path-fail-before.md: EXIT_CODE 1 with ExpectedExitCode 1; the stripped census taken immediately before the run shows `TryInvokeSink(` 0 and `_notifyUnavailable(` 1 unguarded, so the fix was absent; the three refusal-path tests Failed with messages naming the escaping `notify sink failed` exception and the stack naming the unguarded call at the pre-fix line 178; the guard test and all eleven invariant tests Passed at base. Pass-after (refusal-path-pass-after.md): 43/43 | +| Bugfix workflow step 2 (minimal targeted fix) | PASS | One private static helper TryInvokeSink (14 lines), three call-site rewrites, one new message builder, docs; the file split is a pure move (evidence/regression-testing/split-census.md: exactly the eight admitted structural difference lines; fourteen protected signatures and field declarations SPAN-HASH equal=True in evidence/qa-gates/production-edit-scope.md) | +| Bugfix workflow step 3 (verify locally, toolchain in order) | PASS | evidence/qa-gates/toolchain-final-pass.md: pass 1 clean for format, analyzer rebuild, TreatWarningsAsErrors rebuild and the Invoke-MSTestWithCoverage.ps1 route (7388/7388) | +| Design principles (simplicity, reusability, extensibility, separation) | PASS | The duplicated guard blocks of #947 are replaced by one helper that returns the outcome and the exception, so each caller decides the follow-up (forward to the log sink, discard, or record) without a second catch; message builders isolated in their own partial; prime lifecycle isolated in its own partial | +| Classes, functions, APIs | PASS | Internal surface unchanged (constructor, GetPressed, HandleToggleClickAsync, ExecuteToggleAsync, GetPrimeTask signatures byte-equal per SPAN-HASH); the helper is private static with documented contract (sinkCall, sinkFailure, return) | +| Error handling | PASS | Two `catch` clauses remain in the type: the click boundary (EngineToggleStateCoordinator.cs 208) and the sink guard (295); no empty catch remains (the #947 `catch (Exception)` with a comment-only body is gone, census `catch(Exception)` 0); the sink guard is the documented boundary catch accepted at #947 (X-1 below) | +| Logging | PASS | No new logging channel; a notification failure is forwarded to the injected log sink with a dedicated message (BuildNotifyFailedMessage) carrying the engine key | +| File size limit (500 lines) | PASS | Reviewer Read line counts: EngineToggleStateCoordinator.cs 302, Prime.cs 197, Messages.cs 86, EngineToggleStateCoordinatorTests.cs 481, Race.cs 277, SinkGuard.cs 169; all agree with evidence/qa-gates/file-line-counts.md; the four unchanged partials 77, 175, 290, 215 | +| Naming | PASS | PascalCase types and members (TryInvokeSink, BuildNotifyFailedMessage, OnNotify); camelCase locals (notifyFailure, sinkFailure, firstProbe); test names state scenario and outcome | +| Public APIs and compatibility | PASS | No public API; every internal signature unchanged; two Compile items added per project file in the existing Ribbon group | +| Dependencies | PASS | None added | +| I/O boundaries | PASS | No I/O introduced; the type remains host-neutral (class remark lines 33-41 still accurate) | + +## 3. Language-Specific Code Change Policy Compliance + +Language in scope: C# only. + +| Item | Verdict | Evidence | +|---|---|---| +| Formatting (csharpier via dotnet tool run) | PASS | evidence/qa-gates/csharpier-format.md (Formatted 1640 files, Write Set hashes unchanged) and csharpier-check-final.md (Checked 1640 files, exit 0, no path listed) | +| Linting (analyzer rebuild, /t:Rebuild, EnableNETAnalyzers, EnforceCodeStyleInBuild) | PASS | evidence/qa-gates/msbuild-analyzer-final.md: exit 0, ERRORS 0, WARNINGS 0, CSC_OUT 2/2, WRITESET_DIAGNOSTIC_LINES 0 | +| Type checking (TreatWarningsAsErrors rebuild, no /p:Nullable=enable) | PASS | evidence/qa-gates/msbuild-nullable-final.md: exit 0, 0 errors, 0 warnings, CSC_OUT 2/2; command text matches CLAUDE.md character for character | +| Nullable annotations | PASS | No changed file carries #nullable enable (reviewer Grep: 0 in the three production files); no directive added or removed; the `out Exception sinkFailure` assigned null is legal under the files' pre-existing nullable-disabled state (observation O-4 notes the annotation to use if the files ever opt in) | +| Partial-class split | PASS | All three files declare `internal sealed partial class EngineToggleStateCoordinator` in namespace TaskMaster (census 3 of 3); `using` sets trimmed to what each partial needs (System.Globalization and System.Threading moved with their consumers) | +| XML docs on non-obvious contract | PASS | TryInvokeSink summary, both params, returns and remarks; GetPrimeTask summary and returns describe the registration marker; constructor parameter docs state the accessor's non-throwing precondition and both sinks' guarded behaviour; every comment that counts the type's catch clauses matches the two that exist | +| Internal surface | PASS | New helper private static; new message builder private static; new harness member internal on the private Harness | +| Analyzer suppressions | PASS | None added (Grep over the six .cs files for #pragma, SuppressMessage: 0 in changed lines) | + +## 4. Language-Specific Unit Test Policy Compliance + +| Item | Verdict | Evidence | +|---|---|---| +| MSTest framework | PASS | [TestMethod] on all four new tests inside the existing [TestClass] partial fixture; MSTest namespace imported once | +| Moq for mocks | PASS | The strict Mock from the shared Harness; SetupSequence for the two faulted primes; VerifyNoOtherCalls in the invokes-nothing test | +| FluentAssertions | PASS | All assertions use Should() (NotThrowAsync, ContainSingle, BeSameAs, Contain, BeEmpty, NotBeSameAs, HaveCount); no MSTest Assert introduced | +| Repo-wide coverage floors | PASS | 85.96% lines (floor 80% per CLAUDE.md, 85% per rules), 80.10% branches (floor 75%) | +| New module/class/method >= 90% | PASS | TryInvokeSink 100% (10/10 lines, 2/2 branches), BuildNotifyFailedMessage 100% (8/8 lines, no branch) | +| No regression on changed lines | PASS | Every rewritten refusal-path and catch-body line hits=1; coordinator type 203/203 lines; branches 43/44 with the single uncovered arm identical to baseline's | +| Prohibited behaviors (sleeps, retries, timing hacks, weakened assertions) | PASS | 0 banned-API hits; the primary fixture change adds an `OnNotify` hook after the record (lines 414-418) and weakens nothing; the Race partial change is remark-only (NON-DOC-CHANGES 0); the four other partials byte-identical to base | +| Test toolchain route | PASS | Step 4 ran the CLAUDE.md route `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1` at both stages (RAW: False; DOCUMENT_PRESENT, TRX_PRESENT, SUMMARY_FILE_PRESENT all True) | + +## 5. Test Coverage Detail + +| File | Change type | Coverage observation | Disposition | +|---|---|---|---| +| TaskMaster/Ribbon/EngineToggleStateCoordinator.cs | Modified production (496 -> 302 lines after the move; fix applied here) | Class node line-rate 1 (89/89), branch-rate 1; HandleToggleClickAsync 24/24 lines (span 184-212), TryInvokeSink 10/10 (span 287-300), every refusal-path line 186-201 hits=1 | PASS | +| TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs | Added production (pure move of GetPrimeTask, StartPrimeIfNeeded, StartObservedPrime, ApplyPrimeAsync, CompletePrime; CompletePrime's sink call rewritten) | Class node line-rate 1 (72/72), branch-rate 1; CompletePrime 22/22 lines (span 165-195) including the record line 190 and the TryRemove line 194 | PASS | +| TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs | Added production (pure move of the token and five builders; BuildNotifyFailedMessage added) | Class node line-rate 1 (42/42); BuildNotifyFailedMessage 8/8 (span 50-58); branch-rate 0.5 because the file's only branch, RenderEngineName's null-or-empty ternary (line 20), has a never-exercised true arm that was also the single uncovered branch of the pre-split file (baseline node 39/40) | PASS on lines and no-regression; the pre-existing arm is CR-1 (non-blocking) | +| TaskMaster/TaskMaster.csproj | Modified project file (+2 Compile items, lines 467-468) | Not a source file | Discovery proven: three class nodes in the final document, one per file | +| TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs | Added test (169 lines) | Outside the denominator by policy | Not measured; 4/4 Passed, 3 fail-before captured | +| TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs | Modified test fixture (+12 / -1: OnNotify hook) | Outside the denominator by policy | Not measured; 43/43 Passed | +| TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs | Modified test (remark only, +4 / -4) | Outside the denominator by policy | Not measured; NON-DOC-CHANGES 0 | +| TaskMaster.Test/TaskMaster.Test.csproj | Modified project file (+1 Compile item, line 364) | Not a source file | Discovery proven: all four new tests appear in the P1-T24 and P2-T5 runs | + +Package-level projection (TaskMaster package): LINE missed 802 / covered 2503; BRANCH missed 211 / covered 523 after the change (evidence/qa-gates/coverage-final.md), from 802 / 2477 and 211 / 519 at baseline. + +## 6. Test Execution Metrics + +| Run | Scope | Total | Passed | Failed | Source | +|---|---|---|---|---|---| +| Baseline (P0-T14, Invoke-MSTestWithCoverage.ps1) | nine test assemblies | 7384 | 7384 | 0 | evidence/baseline/coverage-baseline.md | +| Final (P2-T5, Invoke-MSTestWithCoverage.ps1) | nine test assemblies | 7388 | 7388 | 0 | evidence/qa-gates/coverage-final.md | +| Fixture baseline (P0-T13) | EngineToggleStateCoordinatorTests | 39 | 39 | 0 | evidence/baseline/coordinator-tests-baseline.md | +| Split fixture green (P1-T8, after the pure move) | EngineToggleStateCoordinatorTests | 39 | 39 | 0 | evidence/regression-testing/split-fixture-green.md | +| Refusal-path fail-before (P1-T14, fix absent) | EngineToggleStateCoordinatorTests | 43 | 40 | 3 (expected) | evidence/regression-testing/refusal-path-fail-before.md | +| Refusal-path pass-after (P1-T24) | EngineToggleStateCoordinatorTests | 43 | 43 | 0 | evidence/regression-testing/refusal-path-pass-after.md | + +Figures compared: final total equals baseline plus 4 (the three refusal-path tests and the guard test); error, timeout, aborted and notExecuted each 0 at both stages (summaries derived from the trx); no Sequence file in any run; FINAL-FAILED-FQN-COUNT 0. + +## 7. Code Quality Checks + +| Check | Command or method | Result | Verdict | +|---|---|---|---| +| Confidentiality masking scan | Executor CMD-HYGIENE at P2-T9 and P2-T20 (ACCOUNT_HITS, MACHINE_HITS, DRIVE_USERS_HITS over 33 then 36 files); reviewer Read of every evidence file cited in this audit | 0 / 0 / 0; the one absolute path in a trx failure message is transcribed as REDACTED-PATH | PASS | +| Raw document scan | Glob over the feature folder for every file; .gitignore lines 146, 147, 150 | 36 Markdown files, no trx, Cobertura or log; raw documents gitignored under coverage/ | PASS | +| Suppression scan (added lines) | Read of the six .cs files and the caller's verbatim diff | No new #pragma, [SuppressMessage], [ExcludeFromCodeCoverage] or analyzer suppression | PASS | +| Workflow change scan | Caller name-status and footprint-scope.md | No .github/, scripts/ or runsettings path changed; project-file changes are Compile items only | PASS | +| Prohibited-construct scan | Reviewer Grep over the seven fixture partials | Thread.Sleep 0, Task.Delay 0, DateTime.Now/UtcNow 0, Stopwatch 0, SpinWait 0, .Wait() 0, .Result 0, Path.GetTemp 0, DoNotParallelize 0 | PASS | +| Catch-clause census | Reviewer Grep for `catch` over the three production files | Two code occurrences: EngineToggleStateCoordinator.cs 208 (click boundary) and 295 (sink guard); every other hit is a documentation mention; Prime.cs and Messages.cs contain no catch | PASS | +| Sink call-site census | Reviewer Read of the three production files | `_logError(` three times, each inside a TryInvokeSink lambda (EngineToggleStateCoordinator.cs 196, 210; Prime.cs 185); `_notifyUnavailable(` once, inside a TryInvokeSink lambda (190); agrees with the executor census (`TryInvokeSink(` 5 = one definition + four call sites) | PASS | +| Comment-drift census (AC7) | Reviewer Grep for the nine retired phrases over the three production files; Read of each surviving phrase | Retired phrases 0 hits; every surviving phrase present at the documented location (several wrap across two source lines, so a line-oriented Grep undercounts them; confirmed by reading) | PASS | +| Tonality scan | Read of issue.md, the plan's evidence citations and the committed evidence | Neutral, factual wording; no humor, hyperbole or metaphor | PASS | + +## 8. Gaps and Exceptions + +- X-1 (accepted exception, carried from #947): TryInvokeSink catches Exception broadly without re-raising. Under CLAUDE.md C#4 this is a boundary catch: the enclosed statement is an injected sink that is the type's last reporting channel (or, for the notification sink, whose failure is forwarded to that last channel), the method remarks document the discard and the forwarding, and the RibbonCommandBoundary.ReportFailure / SafeLog precedent (RibbonCommandBoundary.cs lines 80-113) has the same shape. The General Code Change Policy's letter ("re-raise or propagate") is not met; the alternative is the defect itself. Not Blocking. +- CR-1 (non-blocking Minor, related, autonomous): RenderEngineName's null-or-empty arm is unexercised (pre-existing; now the only branch in the new Messages partial, so that node reads 50% branches while the type reads 97.73%). One refusal-path test with a null engine key closes it; see code-review.2026-10-03T08-50.md. +- PR context artifact pair absent in the review worktree: scope verified from three agreeing sources instead (section Evidence Location Compliance). +- Canonical C# coverage artifact path absent: committed projections and the local raw documents used, per the standing ruling (observation O-3, recurring across #947, #948, #950, #968). +- The P1-T22 phrase census was run by the coordinator under a maintainer one-time bypass rather than by the executor (recorded with provenance in evidence/qa-gates/production-edit-scope.md); this review re-derived the census by Grep and Read and found every value as recorded. +- quality-tiers.yml absent at the repository root (Glob: none; pre-existing; tier gates unevaluable; already promoted by the #956 review). Not attributable to this item. + +## 9. Summary of Changes + +- TaskMaster/Ribbon/EngineToggleStateCoordinator.cs: class made partial; message builders and prime lifecycle moved out; TryInvokeSink added; the refusal path routes the notification through the guard and forwards a notification failure to the log sink through a second guarded call; the click-boundary catch body routes its log call through the guard; constructor, GetPressed, HandleToggleClickAsync and the catch-inventory comments rewritten to match. +- TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs (new): GetPrimeTask with the corrected registration-marker documentation; StartPrimeIfNeeded, StartObservedPrime and ApplyPrimeAsync moved verbatim; CompletePrime records a reported fault kind only in the branch taken when TryInvokeSink returns true, with the #948 placement documented. +- TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs (new): NullEngineNameToken, RenderEngineName and the five builders, with BuildNotifyFailedMessage added. +- TaskMaster/TaskMaster.csproj: two Compile items. +- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs (new): three refusal-path regression tests (fail-before recorded) and one record-placement guard test. +- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs: OnNotify hook on the Harness, invoked after the notification is recorded. +- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs: one remark corrected for the #948 suppression rule. +- TaskMaster.Test/TaskMaster.Test.csproj: one Compile item. +- Evidence: 34 Markdown files under the feature folder's evidence tree plus issue.md check-offs and the checked plan; no raw document committed. + +## 10. Compliance Verdict + +PASS. Every policy area evaluates PASS over the full branch diff. Zero blocking findings; nothing requires a remediation cycle before the pull request is opened. One non-blocking Minor finding (CR-1) and three Informational findings are recorded in the code review for the coordinator's related-defect disposition. The pull request body should carry `Closes #964`. + +## Appendix A: Test Inventory + +| Test class | Test | Status after change | AC | +|---|---|---|---| +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow | Failed before the fix (fix absent), Passed after | AC1 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing | Failed before the fix, Passed after | AC2 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow | Failed before the fix, Passed after | AC2 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain | Passed before and after (guard) | AC3, AC4 | +| EngineToggleStateCoordinatorTests (ThrowingSink partial, unchanged) | GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime, GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime, GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared, HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport | Passed, unchanged | AC3, AC4 | +| EngineToggleStateCoordinatorTests (PrimeFaultOrdering, PrimeRegistration, RepeatFaultSuppression partials, unchanged) | seven invariant tests | Passed, unchanged | AC4 | +| EngineToggleStateCoordinatorTests (Race partial, remark only) | GetPressed_WhenPrimeIsCanceled_LogsErrorAndClearsPrimeMarker and siblings | Passed, unchanged behaviour | AC4 | +| EngineToggleStateCoordinatorTests (primary partial) | HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing and the other pre-existing tests | Passed (OnNotify hook inert when unset) | AC2, AC4 | + +Test method count: 7384 at baseline, 7388 after the change (four added, none removed); fixture 39 to 43. + +## Appendix B: Toolchain Commands Reference + +| Step | Command (as recorded in evidence/qa-gates/toolchain-final-pass.md) | Exit | Iteration | +|---|---|---|---| +| 1 | dotnet tool run csharpier format . | 0 | 1 | +| 1b | dotnet tool run csharpier check . | 0 | 1 | +| 2 | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true | 0 | 1 | +| 3 | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true | 0 | 1 | +| 4 | pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1 (the CLAUDE.md route; 7388/7388; LINE-FLOOR MET, BRANCH-FLOOR MET) | 0 | 1 | + +PowerShell gates (PoshQC MCP format / analyze / test): not run; zero PowerShell files changed on this branch. + +Reviewer commands: none (Bash forbidden). All verification by Read, Grep and Glob against the item worktree, the caller's verbatim diff, the gitignored raw Cobertura documents and the worktree reflog. From 6b8e935c177128d2f455f7bcd2fedc7deff6e30f Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 08:44:10 -0400 Subject: [PATCH 12/17] docs(964): open remediation cycle 1 for related review findings CR-1 and CR-4 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- .../remediation-inputs.2026-10-03T08-43.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-inputs.2026-10-03T08-43.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-inputs.2026-10-03T08-43.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-inputs.2026-10-03T08-43.md new file mode 100644 index 000000000..a8b9b27c0 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-inputs.2026-10-03T08-43.md @@ -0,0 +1,39 @@ +# Remediation Inputs - Issue 964 (cycle 1) + +Timestamp: 2026-10-03T08-43 +Issue: 964 +Branch: bug/engine-toggle-coordinator-947-review-residuals-964 +Opened by: orchestrator, under the maintainer related-defect remediation directive +Source review: code-review.2026-10-03T08-50.md (verdict PASS, 0 blocking findings) +Base for this cycle: HEAD 96d2975d4 (review artifacts committed) + +## Why this cycle exists + +The reduced audit returned PASS with no blocking findings. The maintainer directive for this run states that a defect related to the item's work (same files, same component, same root cause or a sibling call site, including comment drift and test-quality nits in touched files) is remediated inside the item through the normal plan, test and review flow. Two findings in the source review meet that definition. Both are classified `autonomous`. The remaining findings (CR-2, CR-3) record a "No change" recommendation and the observations (O-1 to O-5) are informational or already tracked; they are out of scope for this cycle. + +## Findings + +### R-1 (from CR-1): untested null-or-empty engine-name arm + +- Severity: Non-blocking (Minor); remediated under the related-defect directive +- Remediability: autonomous +- File: `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs`, line 20 (`RenderEngineName`), reached from `BuildUnavailableMessage` and `BuildUnmappedKeyMessage`. +- Defect: the true arm of `string.IsNullOrEmpty(engineName) ? NullEngineNameToken : engineName` is executed by no test. The final Cobertura class node for `EngineToggleStateCoordinator.Messages.cs` reads branch-rate 0.5 (the type aggregate is 43/44 branches). +- Required outcome: a deterministic MSTest test in `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (not the primary fixture, which is at 481/500 lines) covering both a null and an empty engine name on the refusal path (engines accessor returns null): `HandleToggleClickAsync` does not throw; exactly one notification is delivered and it contains the `NullEngineNameToken` rendering `(null)`; no error is logged; no engine member is invoked; no control is invalidated. A `[DataRow(null)]` / `[DataRow("")]` pair is acceptable. +- Invariant to hold: the Messages partial's class node reaches branch-rate 1 in the final coverage document; no production file changes (this is a test-only remediation). + +### R-2 (from CR-4): asymmetric assertions in the both-sinks-throw test + +- Severity: Non-blocking (Informational); remediated under the related-defect directive as a test-quality nit in a touched file +- Remediability: autonomous +- File: `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`, lines 88-112 (`HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`). +- Defect: unlike its sibling test (lines 52-79), it does not assert `Engines.VerifyNoOtherCalls()` or that `Invalidations` is empty. +- Required outcome: add both assertions with FluentAssertions or Moq verification as the sibling does; the test continues to pass. + +## Constraints + +- Test-only change set: `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` only; no production file, csproj or other test file changes. The SinkGuard partial must stay at or below 500 lines. +- MSTest, Moq and FluentAssertions; no temporary files; no `Thread.Sleep` / `Task.Delay`; deterministic. +- Full CLAUDE.md C# toolchain in order (csharpier format and check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild`, `Invoke-MSTestWithCoverage.ps1`) with numeric coverage evidence, restart from step 1 on any failure or file change. +- Evidence under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence//` only. +- Do not weaken or edit any acceptance criterion in `issue.md`. From b27cf3bd24b6cf8c5ce9275488bb143e36a61d33 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 09:32:42 -0400 Subject: [PATCH 13/17] test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013cov8xWT2homg3SU8L3MMT --- ...neToggleStateCoordinatorTests.SinkGuard.cs | 41 ++ .../evidence/other/cycle1-commit-record.md | 15 + .../evidence/other/cycle1-finding-closure.md | 10 + .../other/cycle1-reduced-audit-handoff.md | 72 +++ .../qa-gates/cycle1-coverage-comparison.md | 23 + .../evidence/qa-gates/cycle1-coverage.md | 89 ++++ .../qa-gates/cycle1-csharpier-check.md | 9 + .../qa-gates/cycle1-csharpier-format.md | 12 + .../qa-gates/cycle1-evidence-hygiene.md | 9 + .../evidence/qa-gates/cycle1-footprint.md | 26 ++ .../evidence/qa-gates/cycle1-line-counts.md | 16 + .../qa-gates/cycle1-msbuild-analyzer.md | 14 + .../qa-gates/cycle1-msbuild-nullable.md | 14 + .../qa-gates/cycle1-toolchain-pass.md | 11 + .../regression-testing/cycle1-build.md | 12 + .../regression-testing/cycle1-fixture-run.md | 29 ++ .../regression-testing/cycle1-format.md | 12 + .../regression-testing/cycle1-r1-edit.md | 15 + .../regression-testing/cycle1-r2-edit.md | 10 + .../cycle1-sinkguard-diff.md | 13 + .../regression-testing/cycle1-token-gates.md | 12 + .../fail-before-exception.2026-10-03T09-23.md | 20 + .../remediation-baseline/bootstrap-probe.md | 12 + .../coordinator-tests-baseline.md | 29 ++ .../remediation-baseline/coverage-baseline.md | 15 + .../csharpier-check-baseline.md | 10 + .../msbuild-analyzer-baseline.md | 16 + .../msbuild-nullable-baseline.md | 16 + .../phase0-instructions-read.md | 16 + .../remediation-baseline/scope-and-anchor.md | 30 ++ .../sinkguard-partial-baseline.md | 37 ++ .../remediation-plan.2026-10-03T08-43.md | 442 ++++++++++++++++++ 32 files changed, 1107 insertions(+) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-commit-record.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-finding-closure.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-reduced-audit-handoff.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage-comparison.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-check.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-format.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-evidence-hygiene.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-footprint.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-line-counts.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-analyzer.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-nullable.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-toolchain-pass.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-build.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-fixture-run.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-format.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r1-edit.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r2-edit.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-sinkguard-diff.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-token-gates.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/fail-before-exception.2026-10-03T09-23.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/bootstrap-probe.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coordinator-tests-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coverage-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/csharpier-check-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-analyzer-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-nullable-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/phase0-instructions-read.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/scope-and-anchor.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/sinkguard-partial-baseline.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md diff --git a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs index 03937f571..0dfb38e4b 100644 --- a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +++ b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs @@ -109,10 +109,51 @@ public async Task HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngin notifyFailure, "the log sink receives the notification failure unchanged" ); + harness.Engines.VerifyNoOtherCalls(); + harness.Invalidations.Should().BeEmpty("a refused click changes no state to display"); } #endregion Issue #964 — a throwing notification sink on the refusal path + #region Issue #964 — the refusal path with a null or empty engine key + + /// + /// Refusal path for an unusable engine key: with the engines unavailable, a null or empty + /// key is rendered as the (null) token in the one notification, the click does not + /// throw, nothing is logged, no engine member is invoked and no control is invalidated. + /// Exercises the null-or-empty arm of the engine-name renderer through the notification + /// message builder. + /// + [DataTestMethod] + [DataRow(null)] + [DataRow("")] + public async Task HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing( + string engineName + ) + { + // Arrange: the pre-SetGlobals window, with sinks that record and do not throw. + var harness = new Harness { EnginesAvailable = false }; + + // Act + Func act = () => harness.Coordinator.HandleToggleClickAsync(engineName); + + // Assert + await act.Should() + .NotThrowAsync("a refused click with an unusable key must degrade quietly"); + harness + .Notifications.Should() + .ContainSingle("exactly one notice per refused toggle click"); + harness + .Notifications[0] + .Should() + .Contain("(null)", "an unusable key is rendered as the null-engine-name token"); + harness.Errors.Should().BeEmpty("a refused click is not a fault"); + harness.Engines.VerifyNoOtherCalls(); + harness.Invalidations.Should().BeEmpty("a refused click changes no state to display"); + } + + #endregion Issue #964 — the refusal path with a null or empty engine key + #region Issue #964 — the issue #948 record placement under the shared guard /// diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-commit-record.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-commit-record.md new file mode 100644 index 000000000..4e3edb822 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-commit-record.md @@ -0,0 +1,15 @@ +# Pre-commit state (P2-T12) + +Timestamp: 2026-10-03T09-32 +Command: git status --porcelain --untracked-files=all; git rev-parse --abbrev-ref HEAD; git rev-parse HEAD +EXIT_CODE: 0 +Output Summary: branch and pre-commit head recorded; every porcelain entry is the SinkGuard path, lies under the feature folder, or lies under .claude/agent-memory/. + +BRANCH: bug/engine-toggle-coordinator-947-review-residuals-964 +PRE-COMMIT-HEAD: 6b8e935c177128d2f455f7bcd2fedc7deff6e30f + +PORCELAIN (before staging): ` M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`; four untracked files under .claude/agent-memory/ (atomic-planner and orchestrator, ambient, never staged); untracked files under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/ (the plan file and the evidence artifacts written by P0-T1 to P2-T11). + +Staging rule: explicit paths only, namely TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs and the feature folder docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964. Never git add -A or git add . +Commit subject: test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions +The commit hash and the push output are reported in the executor return and are not written into the repository (D-7). No commit-time value is claimed here. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-finding-closure.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-finding-closure.md new file mode 100644 index 000000000..8349d0f03 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-finding-closure.md @@ -0,0 +1,10 @@ +# Closure of findings R-1 and R-2 (P2-T10) + +Timestamp: 2026-10-03T09-31 +Command: Read of evidence/regression-testing/cycle1-r2-edit.md, cycle1-r1-edit.md, cycle1-fixture-run.md, cycle1-sinkguard-diff.md and evidence/qa-gates/cycle1-coverage.md, cycle1-footprint.md +EXIT_CODE: 0 +Output Summary: +R-1: MET. The TOKENS-R1 counts hold (regression-testing/cycle1-r1-edit.md, cycle1-token-gates.md); R1-NAME ran rows=2 passed=2 (cycle1-fixture-run.md); the Messages CLASS-NODE reads branch-rate=1 with COORD-BRANCHES covered=44 valid=44 (qa-gates/cycle1-coverage.md); the SinkGuard partial is the only changed code file (qa-gates/cycle1-footprint.md). +R-2: MET. Both TOKENS-R2 first-edit counts are 2 and the two lines sit inside the both-sinks-throw test (R2-PLACEMENT: 112 and 113, regression-testing/cycle1-r2-edit.md); R2-NAME reads rows=1 passed=1 (cycle1-fixture-run.md); the numstat shows 0 deleted lines (cycle1-sinkguard-diff.md). + +AC1 to AC8 of issue.md are unchanged and still checked (remediation-baseline/scope-and-anchor.md: 8 checked, 0 unchecked; qa-gates/cycle1-footprint.md: issue.md in neither listing). diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-reduced-audit-handoff.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-reduced-audit-handoff.md new file mode 100644 index 000000000..cbb1d07ff --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-reduced-audit-handoff.md @@ -0,0 +1,72 @@ +# Reduced audit handoff (P2-T11) + +Timestamp: 2026-10-03T09-32 +Command: Read of the cycle evidence artifacts listed below +EXIT_CODE: 0 +Output Summary: cycle 1 delivered; R-1 MET and R-2 MET; evidence paths listed; reduced audit checks stated. + +## Sources + +- Finding source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md (findings CR-1 and CR-4) +- AC source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md (unchanged) + +## Statuses (copied from evidence/other/cycle1-finding-closure.md) + +- R-1: MET +- R-2: MET + +## Evidence paths (all under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/) + +remediation-baseline/ +- remediation-baseline/phase0-instructions-read.md +- remediation-baseline/scope-and-anchor.md +- remediation-baseline/sinkguard-partial-baseline.md +- remediation-baseline/coverage-baseline.md +- remediation-baseline/bootstrap-probe.md +- remediation-baseline/csharpier-check-baseline.md +- remediation-baseline/msbuild-analyzer-baseline.md +- remediation-baseline/msbuild-nullable-baseline.md +- remediation-baseline/coordinator-tests-baseline.md + +regression-testing/ +- regression-testing/cycle1-r2-edit.md +- regression-testing/cycle1-r1-edit.md +- regression-testing/cycle1-format.md +- regression-testing/cycle1-token-gates.md +- regression-testing/cycle1-build.md +- regression-testing/cycle1-fixture-run.md +- regression-testing/fail-before-exception.2026-10-03T09-23.md +- regression-testing/cycle1-sinkguard-diff.md + +qa-gates/ +- qa-gates/cycle1-csharpier-format.md +- qa-gates/cycle1-csharpier-check.md +- qa-gates/cycle1-msbuild-analyzer.md +- qa-gates/cycle1-msbuild-nullable.md +- qa-gates/cycle1-coverage.md +- qa-gates/cycle1-coverage-comparison.md +- qa-gates/cycle1-toolchain-pass.md +- qa-gates/cycle1-footprint.md +- qa-gates/cycle1-line-counts.md + +other/ +- other/cycle1-finding-closure.md +- other/cycle1-reduced-audit-handoff.md (this artifact) + +## WRITTEN AFTER THIS RECORD + +- docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-commit-record.md (written by P2-T12) +- docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-evidence-hygiene.md (written by P2-T13) + +No result is claimed for either. + +## Reduced artifact checks for the auditor + +- The fixture run (regression-testing/cycle1-fixture-run.md): 45 of 45 passed, R1-NAME 2 rows passed. +- The coverage comparison with the class-node read-out (qa-gates/cycle1-coverage-comparison.md, qa-gates/cycle1-coverage.md): coordinator branches 44/44, Messages class-node branch-rate 0.5 to 1. +- The footprint gate (qa-gates/cycle1-footprint.md): the SinkGuard partial is the only changed code file. +- The hygiene gate (qa-gates/cycle1-evidence-hygiene.md), to be read once written. + +## Out of scope + +CR-2, CR-3 and observations O-1 to O-5 are out of scope for this cycle. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage-comparison.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage-comparison.md new file mode 100644 index 000000000..e537b3d5b --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage-comparison.md @@ -0,0 +1,23 @@ +# Coverage comparison (P2-T6) + +Timestamp: 2026-10-03T09-30 +Command: Read of evidence/remediation-baseline/coverage-baseline.md and evidence/qa-gates/cycle1-coverage.md +EXIT_CODE: 0 +Output Summary: coordinator lines unchanged at 203/203; coordinator branches 43/44 to 44/44; Messages class-node branch-rate 0.5 to 1; both floors met; every clause MET. + +BASELINE-FIRST-PARTY: First-party coverage: lines 56629/65881 (85.96%), branches 13683/17082 (80.10%) +FINAL-FIRST-PARTY: First-party coverage: lines 56639/65881 (85.97%), branches 13687/17082 (80.13%) +(Observations only; the gates are the floors recorded in cycle1-coverage.md.) +BASELINE-COORD-LINES: 203/203 +FINAL-COORD-LINES: 203/203 +BASELINE-COORD-BRANCHES: 43/44 (97.73) +FINAL-COORD-BRANCHES: 44/44 (100) +BASELINE-MESSAGES-BRANCH-RATE: 0.5 +FINAL-MESSAGES-BRANCH-RATE: 1 +NEW-CODE-COVERAGE: not applicable, no production line or branch was added + +Clauses: +- Final coordinator line count equals the baseline (no line removed from coverage): MET (203 and 203) +- Final covered branches equal final valid branches and exceed the baseline covered branches by exactly 1: MET (44 equals 44; 44 minus 43 is 1) +- Final Messages class-node branch-rate is 1: MET (1) +- Both final floors met: MET (LINE-FLOOR: MET, BRANCH-FLOOR: MET) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage.md new file mode 100644 index 000000000..4dbe68e6d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage.md @@ -0,0 +1,89 @@ +# Test and coverage gate (P2-T5) + +Timestamp: 2026-10-03T09-30 +Command: pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1 +EXIT_CODE: 0 +Output Summary: +- RUNNER_EXIT_CODE: 0 (empty COLLECT_FAILURE_MESSAGE and THRESHOLD_MESSAGE) +- LINE-FLOOR: MET +- BRANCH-FLOOR: MET +- First-party coverage: lines 56639/65881 (85.97%), branches 13687/17082 (80.13%) +- ROOT line-rate=0.859717 branch-rate=0.801253 lines-covered=56639 lines-valid=65881 +- COORD-LINES covered=203 valid=203 +- COORD-BRANCHES covered=44 valid=44 +- COORD-LINE-RATE: 100 +- COORD-BRANCH-RATE: 100 +- CLASS-NODE TaskMaster/Ribbon/EngineToggleStateCoordinator.cs line-rate=1 branch-rate=1 +- CLASS-NODE TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs line-rate=1 branch-rate=1 +- CLASS-NODE TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs line-rate=1 branch-rate=1 +- FINAL-FAILED-FQN-COUNT: 0 +- FINAL-TEST-TOTAL: 7390 + +Details: +DISCOVERED_LINE: Discovered 9 test assemblies. +THRESHOLD_MESSAGE: (empty) +COLLECT_FAILURE_MESSAGE: (empty) +DOCUMENT_PRESENT: True +TRX_PRESENT: True +SUMMARY_FILE_PRESENT: True +TEST-DEFINITIONS: 7380 + +SUMMARY-BEGIN +Test run outcome: Completed +Total 7390, executed 7390, passed 7390, failed 0. +Skipped 0, derived as total minus executed rather than reported by the test platform. +Figures reported verbatim by the test platform: error 0, timeout 0, aborted 0, notExecuted 0, inconclusive 0. +Failed tests: none +SUMMARY-END + +PROJECTION-BEGIN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +PROJECTION-END + +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.cs nodes=1 covered=89 valid=89 branches-covered=22 branches-valid=22 +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs nodes=1 covered=72 valid=72 branches-covered=20 branches-valid=20 +COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs nodes=1 covered=42 valid=42 branches-covered=2 branches-valid=2 +COORD-CLASS-NODES: 3 + +Class-node Grep read-outs (explicit file paths, git-ignored documents; pattern NODE-RATE-ONE = Messages class node with branch-rate 1, NODE-RATE-HALF = same node with branch-rate 0.5): +- coverage/remediation-964.cobertura.xml: NODE-RATE-ONE = 1, NODE-RATE-HALF = 0 +- coverage/final-964.cobertura.xml (pre-change control): NODE-RATE-ONE = 0, NODE-RATE-HALF = 1 + +The raw Cobertura and trx documents remain under the git-ignored coverage directory and are not committed. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-check.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-check.md new file mode 100644 index 000000000..8106d9b9a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-check.md @@ -0,0 +1,9 @@ +# Repository-wide format check (P2-T2) + +Timestamp: 2026-10-03T09-24 +Command: dotnet tool run csharpier check . +EXIT_CODE: 0 +Output Summary: Checked 1640 files in 7110ms. No path reported as unformatted. + +CSHARPIER_EXIT_CODE: 0 +Checked 1640 files in 7110ms. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-format.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-format.md new file mode 100644 index 000000000..f56ad1ae4 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-csharpier-format.md @@ -0,0 +1,12 @@ +# Repository-wide format (P2-T1) + +Timestamp: 2026-10-03T09-24 +Command: CMD-HASH-SINKGUARD and git status --porcelain --untracked-files=all before; dotnet tool run csharpier format .; CMD-HASH-SINKGUARD and git status --porcelain --untracked-files=all after +EXIT_CODE: 0 +Output Summary: format exit 0, "Formatted 1640 files in 4134ms." (processed count); SinkGuard hash identical before and after; porcelain listings identical before and after. Pass 1; no restart needed. + +PASS: 1 +HASH-BEFORE: 825680DF796E329331596E30D0F66CB924AFB0996494C19FC8B356CDC426142D +HASH-AFTER: 825680DF796E329331596E30D0F66CB924AFB0996494C19FC8B356CDC426142D +PORCELAIN-BEFORE and PORCELAIN-AFTER (identical): ` M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`, the four untracked `.claude/agent-memory/` files, and untracked files under the feature folder (the evidence artifacts written so far and the plan file). +CSHARPIER_EXIT_CODE: 0 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-evidence-hygiene.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-evidence-hygiene.md new file mode 100644 index 000000000..ba727b37b --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-evidence-hygiene.md @@ -0,0 +1,9 @@ +# Evidence hygiene sweep (P2-T13) + +Timestamp: 2026-10-03T09-32 +Command: CMD-HYGIENE (host-identifier sweep over every Markdown file under the feature folder; host tokens derived at run time and not recorded) +EXIT_CODE: 0 +Output Summary: FILES_SCANNED=70 ACCOUNT_HITS=0 MACHINE_HITS=0 DRIVE_USERS_HITS=0 RAW_DOCUMENTS=0 + +FILES_SCANNED=70 ACCOUNT_HITS=0 MACHINE_HITS=0 DRIVE_USERS_HITS=0 RAW_DOCUMENTS=0 +HIT-FILE rows: none diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-footprint.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-footprint.md new file mode 100644 index 000000000..9b6900c0a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-footprint.md @@ -0,0 +1,26 @@ +# Change footprint (P2-T8) + +Timestamp: 2026-10-03T09-31 +Command: git diff --name-only 6b8e935c177128d2f455f7bcd2fedc7deff6e30f; git status --porcelain --untracked-files=all; git diff --name-only 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster TaskMaster.Test; git diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test +EXIT_CODE: 0 +Output Summary: the only changed tracked path is the SinkGuard partial; every untracked entry lies under the feature folder or .claude/agent-memory/; issue.md is in neither listing; the negative control lists the first-cycle coordinator production path, so the path-set rule can fail. + +LISTING 1 (diff --name-only against the cycle base): +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs + +PORCELAIN (--untracked-files=all): ` M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`; four untracked `.claude/agent-memory/` files (ambient, never staged); untracked files all under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/ (evidence artifacts and remediation-plan.2026-10-03T08-43.md). + +LISTING 3 (diff --name-only against the cycle base, TaskMaster and TaskMaster.Test): exactly one line, TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs + +issue.md appears in neither listing. +Positive control: the union of listing 1 and the porcelain contains the SinkGuard path and docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md. + +NEGATIVE CONTROL (diff --name-only against 94287369908cc920b21b0e3256314f988ad7d2f5, TaskMaster and TaskMaster.Test), non-empty and contains TaskMaster/Ribbon/EngineToggleStateCoordinator.cs: +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs +TaskMaster.Test/TaskMaster.Test.csproj +TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs +TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs +TaskMaster/Ribbon/EngineToggleStateCoordinator.cs +TaskMaster/TaskMaster.csproj diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-line-counts.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-line-counts.md new file mode 100644 index 000000000..05c55a6b3 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-line-counts.md @@ -0,0 +1,16 @@ +# Fixture partial line counts (P2-T9) + +Timestamp: 2026-10-03T09-31 +Command: CMD-LINECOUNT (Get-ChildItem over TaskMaster.Test\Ribbon filtered to EngineToggleStateCoordinatorTests*.cs, Get-Content line counts) +EXIT_CODE: 0 +Output Summary: 7 partials, all at most 500 lines; SinkGuard 210 (from 169); primary fixture 481 (unchanged). + +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.cs = 481 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.PrimeFaultOrdering.cs = 77 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.PrimeRegistration.cs = 175 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.Race.cs = 277 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.RepeatFaultSuppression.cs = 290 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs = 210 +LINES TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.ThrowingSink.cs = 215 +TEST-PARTIALS: 7 +SINKGUARD-LINES-FINAL: 210 (equals SINKGUARD-LINES-AFTER: 210 of P1-T4) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-analyzer.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-analyzer.md new file mode 100644 index 000000000..ee14ef0f4 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-analyzer.md @@ -0,0 +1,14 @@ +# Analyzer gate (P2-T3) + +Timestamp: 2026-10-03T09-25 +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true +EXIT_CODE: 0 +Output Summary: rebuild exit 0, 0 errors, 0 warnings (baseline ANALYZER-BASELINE-WARNINGS: 0), compiler ran for both projects, no coordinator or SinkGuard diagnostic. + +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 (ANALYZER-BASELINE-WARNINGS: 0) +CSC_OUT_TASKMASTER: 2 +CSC_OUT_TASKMASTER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +COORDINATOR_DIAGNOSTIC_LINES: 0 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-nullable.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-nullable.md new file mode 100644 index 000000000..1596eafbb --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-msbuild-nullable.md @@ -0,0 +1,14 @@ +# Type-check gate (P2-T4) + +Timestamp: 2026-10-03T09-25 +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true +EXIT_CODE: 0 +Output Summary: rebuild exit 0, 0 errors, 0 warnings (baseline NULLABLE-BASELINE-WARNINGS: 0), compiler ran for both projects, no coordinator or SinkGuard diagnostic. + +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 (NULLABLE-BASELINE-WARNINGS: 0) +CSC_OUT_TASKMASTER: 2 +CSC_OUT_TASKMASTER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +COORDINATOR_DIAGNOSTIC_LINES: 0 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-toolchain-pass.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-toolchain-pass.md new file mode 100644 index 000000000..9dcdf507f --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-toolchain-pass.md @@ -0,0 +1,11 @@ +# Toolchain pass (P2-T7) + +Timestamp: 2026-10-03T09-30 +Command: dotnet tool run csharpier format . (verify: dotnet tool run csharpier check .); msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true; msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true; Invoke-MSTestWithCoverage.ps1 +EXIT_CODE: 0 +Output Summary: single clean pass, pass number 1 (no restart; the SinkGuard hash was unchanged by the repository-wide format). +- Step 1 format and check: qa-gates/cycle1-csharpier-format.md and qa-gates/cycle1-csharpier-check.md, PASS (check: Checked 1640 files, exit 0) +- Step 2 analyzer: qa-gates/cycle1-msbuild-analyzer.md, PASS (0 errors, 0 warnings) +- Step 3 type-check: qa-gates/cycle1-msbuild-nullable.md, PASS (0 errors, 0 warnings) +- Step 4 test and coverage: qa-gates/cycle1-coverage.md, PASS +TEST-STEP: PASS (runner exit 0, 7390 of 7390 passed, zero failed tests) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-build.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-build.md new file mode 100644 index 000000000..95b83e30c --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-build.md @@ -0,0 +1,12 @@ +# Incremental build (P1-T5) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" +EXIT_CODE: 0 +Output Summary: build exit 0, 0 errors, test assembly timestamp advanced, compiler ran for TaskMaster.Test (the production project was up to date). + +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +TEST_DLL_ADVANCED: True +CSC_OUT_TASKMASTER: 0 +CSC_OUT_TASKMASTER_TEST: 2 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-fixture-run.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-fixture-run.md new file mode 100644 index 000000000..9c34cb442 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-fixture-run.md @@ -0,0 +1,29 @@ +# Coordinator fixture run after the edits (P1-T6) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\p1-t6" "/Logger:trx;LogFileName=p1-t6.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 +Output Summary: 45 of 45 passed, 0 failed; R1-NAME ran 2 rows and both passed; R2-NAME and every other named test passed with 1 row. + +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=45 executed=45 passed=45 failed=0 +FIXTURE-TOTAL: 45 +RESULT HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing rows=2 passed=2 +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow rows=1 passed=1 +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing rows=1 passed=1 +RESULT HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain rows=1 passed=1 +RESULT GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns rows=1 passed=1 +RESULT GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared rows=1 passed=1 +RESULT HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport rows=1 passed=1 +RESULT GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly rows=1 passed=1 +RESULT GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly rows=1 passed=1 +RESULT HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing rows=1 passed=1 +FAILED lines: none diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-format.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-format.md new file mode 100644 index 000000000..c14fda9da --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-format.md @@ -0,0 +1,12 @@ +# Format of the SinkGuard partial (P1-T3) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: dotnet tool run csharpier format TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs; dotnet tool run csharpier check TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +EXIT_CODE: 0 +Output Summary: format exit 0 (rewrote the Notifications[0] chain layout); check exit 0 with a line beginning "Checked 1 file" and no path listed. + +FORMAT: +Formatted 1 files in 1378ms. +FORMAT_EXIT_CODE: 0 +Checked 1 files in 470ms. +CHECK_EXIT_CODE: 0 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r1-edit.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r1-edit.md new file mode 100644 index 000000000..90e8740b8 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r1-edit.md @@ -0,0 +1,15 @@ +# R-1 edit (P1-T2) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: Edit tool (E2) on TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs; Grep tool -n over the same file for the P1-T2 patterns. +EXIT_CODE: 0 +Output Summary: DataTestMethod 1, DataRow(null) 1, DataRow("") 1, R1-NAME 1, new region and endregion 1 each, Invalidations assertion 3, issue-948 region line still present once; the new region lies between the first region's endregion (line 116) and the issue-948 region (line 156). + +`\[DataTestMethod\]` = 1 (line 127) +`\[DataRow\(null\)\]` = 1 (line 128) +`\[DataRow\(""\)\]` = 1 (line 129) +R1-NAME = 1 (line 130) +`#region Issue #964 — the refusal path with a null or empty engine key` = 1 (line 118) +`#endregion Issue #964 — the refusal path with a null or empty engine key` = 1 (line 154) +`harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` = 3 (lines 78, 113, 151) +`#region Issue #964 — the issue #948 record placement` = 1 (line 156) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r2-edit.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r2-edit.md new file mode 100644 index 000000000..6cb12dde3 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-r2-edit.md @@ -0,0 +1,10 @@ +# R-2 edit (P1-T1) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: Edit tool (E1) on TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs; Grep tool counts for `harness\.Engines\.VerifyNoOtherCalls\(\);` and `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);`; Read tool over lines 104-114. +EXIT_CODE: 0 +Output Summary: both patterns count 2 (first-edit values); the two new lines sit in the body of the both-sinks-throw test after its BeSameAs chain and before its closing brace. + +`harness\.Engines\.VerifyNoOtherCalls\(\);` = 2 (lines 77, 112) +`harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` = 2 (lines 78, 113) +R2-PLACEMENT: 112 and 113 (inside HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow, which starts at line 88; closing brace at line 114) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-sinkguard-diff.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-sinkguard-diff.md new file mode 100644 index 000000000..78d0fcb97 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-sinkguard-diff.md @@ -0,0 +1,13 @@ +# SinkGuard diff shape against the cycle base (P1-T8) + +Timestamp: 2026-10-03T09-23 +Command: git diff --numstat 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs; git diff --name-only 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster TaskMaster.Test; git status --porcelain --untracked-files=all -- TaskMaster TaskMaster.Test +EXIT_CODE: 0 +Output Summary: 41 added and 0 deleted lines; the only changed code path is the SinkGuard partial; porcelain shows one modified entry. + +NUMSTAT: 41 0 TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +NAME-ONLY (TaskMaster, TaskMaster.Test): +TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +PORCELAIN (TaskMaster, TaskMaster.Test): + M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +Positive control: the SinkGuard path appears in both listings. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-token-gates.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-token-gates.md new file mode 100644 index 000000000..ae97830b9 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/cycle1-token-gates.md @@ -0,0 +1,12 @@ +# Token gates on the formatted SinkGuard partial (P1-T4) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: Grep tool counts over TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs for every pattern of TOKENS-R2, TOKENS-R1 and TOKENS-FORBIDDEN, and for `^`. +EXIT_CODE: 0 +Output Summary: every TOKENS-R2 and TOKENS-R1 count equals its true-after value; forbidden tokens 0; TaskCompletionSource 2; file is 210 lines (within 195 to 500). + +TOKENS-R2 (after): `harness\.Engines\.VerifyNoOtherCalls\(\);` = 3; `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` = 3; `Engines\.VerifyNoOtherCalls` = 3 +TOKENS-R1 (after): `\[DataTestMethod\]` = 1; `\[DataRow\(null\)\]` = 1; `\[DataRow\(""\)\]` = 1; R1-NAME = 1; `\[TestMethod\]` = 4; `#region ` = 3; `#endregion ` = 3 +TOKENS-FORBIDDEN: combined alternation of `Thread\.Sleep`, `Task\.Delay`, `DoNotParallelize`, `GetTempPath`, `File\.`, `DateTime\.Now`, `DateTime\.UtcNow` = 0 (each 0) +Positive control: `TaskCompletionSource` = 2 +SINKGUARD-LINES-AFTER: 210 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/fail-before-exception.2026-10-03T09-23.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/fail-before-exception.2026-10-03T09-23.md new file mode 100644 index 000000000..a08e6551d --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/regression-testing/fail-before-exception.2026-10-03T09-23.md @@ -0,0 +1,20 @@ +# Fail-before exception dossier (cycle 1, P1-T7) + +Timestamp: 2026-10-03T09-23 +Command: sources cited: evidence/remediation-baseline/sinkguard-partial-baseline.md, evidence/remediation-baseline/coverage-baseline.md, evidence/regression-testing/cycle1-token-gates.md, evidence/regression-testing/cycle1-fixture-run.md, evidence/remediation-baseline/coordinator-tests-baseline.md +EXIT_CODE: 0 +Output Summary: no failing run against the unmodified production code exists for R-1 or R-2; the false-before and true-after pairs are recorded below. The class-node half is appended by P2-T6. + +WhyFailingRunImpossible: R-1 adds coverage of a null or empty engine key over production behaviour that is already correct, and R-2 adds assertions over behaviour that is already correct, so no test of either change can fail against the unmodified production code. + +## Alternative proof (false before, true after) + +- R1-NAME count before the edit: 0 (remediation-baseline/sinkguard-partial-baseline.md); the baseline fixture run reports `rows=0 passed=0` for it (remediation-baseline/coordinator-tests-baseline.md). +- BASELINE-MESSAGES-BRANCH-RATE: 0.5 and BASELINE-COORD-BRANCHES: 43/44 (remediation-baseline/coverage-baseline.md). +- TOKENS-R2 before and after (sinkguard-partial-baseline.md, then regression-testing/cycle1-token-gates.md): `harness\.Engines\.VerifyNoOtherCalls\(\);` 1 then 3; `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` 1 then 3; `Engines\.VerifyNoOtherCalls` 1 then 3. +- TOKENS-R1 before and after: `\[DataTestMethod\]` 0 then 1; `\[DataRow\(null\)\]` 0 then 1; `\[DataRow\(""\)\]` 0 then 1; R1-NAME 0 then 1; `#region ` 2 then 3; `#endregion ` 2 then 3. +- FIXTURE-TOTAL: 45 (regression-testing/cycle1-fixture-run.md) against BASELINE-TOTAL: 43 (remediation-baseline/coordinator-tests-baseline.md); R1-NAME runs 2 rows, both passed. + +## CLASS-NODE-PROOF + +Messages class-node branch-rate: baseline value 0.5 (remediation-baseline/coverage-baseline.md, read from the pre-change document), final value 1 (qa-gates/cycle1-coverage.md, read from the post-change document; coordinator branches 44/44). diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/bootstrap-probe.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/bootstrap-probe.md new file mode 100644 index 000000000..97a550aee --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/bootstrap-probe.md @@ -0,0 +1,12 @@ +# Toolchain bootstrap probe (P0-T5) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: pwsh -NoProfile -Command (Set-Location to the worktree; SDK marker test; dotnet --version; dotnet tool list --local; package directory count; dotnet-coverage resolution) +EXIT_CODE: 0 +Output Summary: SDK marker present, SDK 8.0.205 active, csharpier 1.2.6 restored, 172 package directories, dotnet-coverage resolved. + +SDK_MARKER=True +dotnet --version: 8.0.205 +Local tools (Package Id, Version): csharpier 1.2.6 +PACKAGE_DIRS=172 +DOTNET_COVERAGE_RESOLVED=True diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coordinator-tests-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coordinator-tests-baseline.md new file mode 100644 index 000000000..3c2cae789 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coordinator-tests-baseline.md @@ -0,0 +1,29 @@ +# Coordinator fixture baseline (P0-T9) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\p0-t9" "/Logger:trx;LogFileName=p0-t9.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 +Output Summary: 43 of 43 passed, 0 failed; R1-NAME has 0 rows (false-before); every existing SinkGuard and invariant name has 1 passing row. + +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=43 executed=43 passed=43 failed=0 +BASELINE-TOTAL: 43 +RESULT HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing rows=0 passed=0 +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow rows=1 passed=1 +RESULT HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing rows=1 passed=1 +RESULT HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain rows=1 passed=1 +RESULT GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns rows=1 passed=1 +RESULT GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime rows=1 passed=1 +RESULT GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared rows=1 passed=1 +RESULT HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport rows=1 passed=1 +RESULT GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly rows=1 passed=1 +RESULT GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly rows=1 passed=1 +RESULT HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing rows=1 passed=1 +FAILED lines: none diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coverage-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coverage-baseline.md new file mode 100644 index 000000000..90e6b790a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/coverage-baseline.md @@ -0,0 +1,15 @@ +# Coverage baseline (P0-T4) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: Grep tool -n over evidence/qa-gates/coverage-final.md for five committed-evidence patterns; Grep tool counts over coverage/final-964.cobertura.xml (explicit file path) for the Messages class node with branch-rate 0.5 and with branch-rate 1. +EXIT_CODE: 0 +Output Summary: each committed-evidence pattern has exactly one matching line (lines 13, 37, 91, 93, 94 of coverage-final.md); raw document read gives 1 for the 0.5 pattern and 0 for the 1 pattern. + +BASELINE-COORD-BRANCHES: 43/44 (97.73 percent) +BASELINE-COORD-LINES: 203/203 +BASELINE-MESSAGES-LINES: 42/42 +BASELINE-FIRST-PARTY: - First-party coverage: lines 56629/65881 (85.96%), branches 13683/17082 (80.10%) +BASELINE-TEST-TOTAL: 7388 +BASELINE-MESSAGES-BRANCH-RATE: 0.5 + +Raw-document read (coverage/final-964.cobertura.xml, git-ignored, not committed): pattern with branch-rate 0.5 = 1, pattern with branch-rate 1 = 0. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/csharpier-check-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/csharpier-check-baseline.md new file mode 100644 index 000000000..d4b8529ff --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/csharpier-check-baseline.md @@ -0,0 +1,10 @@ +# CSharpier check baseline (P0-T6) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: dotnet tool run csharpier check . +EXIT_CODE: 0 +Output Summary: Checked 1640 files in 6129ms. No path reported as unformatted. + +CSHARPIER_EXIT_CODE: 0 +Checked 1640 files in 6129ms. +Unformatted paths: none diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-analyzer-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-analyzer-baseline.md new file mode 100644 index 000000000..c2b19dd30 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-analyzer-baseline.md @@ -0,0 +1,16 @@ +# Analyzer baseline (P0-T7) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true +EXIT_CODE: 0 +Output Summary: rebuild exit 0, 0 errors, 0 warnings, compiler ran for both projects, no diagnostic naming a coordinator or SinkGuard file. + +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 +ANALYZER-BASELINE-WARNINGS: 0 +CSC_OUT_TASKMASTER: 2 +CSC_OUT_TASKMASTER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +COORDINATOR_DIAGNOSTIC_LINES: 0 +TEST_DLL_EXISTS: True diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-nullable-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-nullable-baseline.md new file mode 100644 index 000000000..b15f563f0 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/msbuild-nullable-baseline.md @@ -0,0 +1,16 @@ +# Nullable baseline (P0-T8) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true +EXIT_CODE: 0 +Output Summary: rebuild exit 0, 0 errors, 0 warnings, compiler ran for both projects, no diagnostic naming a coordinator or SinkGuard file. + +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 +NULLABLE-BASELINE-WARNINGS: 0 +CSC_OUT_TASKMASTER: 2 +CSC_OUT_TASKMASTER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +COORDINATOR_DIAGNOSTIC_LINES: 0 +TEST_DLL_EXISTS: True diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/phase0-instructions-read.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/phase0-instructions-read.md new file mode 100644 index 000000000..9de60718f --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/phase0-instructions-read.md @@ -0,0 +1,16 @@ +# Phase 0 policy reads (remediation cycle 1, issue 964) + +Timestamp: 2026-10-03T09-17 +Command: Read tool over the six policy files listed below, in the order stated. +EXIT_CODE: 0 +Output Summary: six policy documents read from the item worktree; none modified. + +Policy Order: CLAUDE.md, .claude/rules/general-code-change.md, .claude/rules/general-unit-test.md, .claude/rules/csharp.md (then tonality.md and plan-acceptance-gates.md) + +Files read: +- CLAUDE.md +- .claude/rules/general-code-change.md +- .claude/rules/general-unit-test.md +- .claude/rules/csharp.md +- .claude/rules/tonality.md +- .claude/rules/plan-acceptance-gates.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/scope-and-anchor.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/scope-and-anchor.md new file mode 100644 index 000000000..040907d5a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/scope-and-anchor.md @@ -0,0 +1,30 @@ +# Scope and anchor (P0-T2) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: git status --porcelain --untracked-files=all; git merge-base 6b8e935c177128d2f455f7bcd2fedc7deff6e30f HEAD; git diff --exit-code --stat 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster TaskMaster.Test; Grep tool over issue.md (patterns `^- Work Mode: minor-audit`, `^## Acceptance Criteria`, `^- \[x\] AC[1-8] `, `^- \[ \] AC`); Read/Grep probes for spec.md, user-story.md, research*.md in the feature folder. +EXIT_CODE: 0 +Output Summary: merge-base equals the cycle base; code diff against the cycle base is empty; issue.md counts 1, 1, 8, 0; no spec.md, user-story.md or research*.md; inherited porcelain entries all under the feature folder or .claude/agent-memory/. + +MERGE-BASE: 6b8e935c177128d2f455f7bcd2fedc7deff6e30f +ANCHOR-CODE-DIFF-EXIT=0 +ISSUE-GREP-COUNTS: work-mode=1, ac-heading=1, checked-AC1-8=8, unchecked-AC=0 +REQUIREMENTS-DOCUMENTS: none (Glob returned none; Read of spec.md and user-story.md reported not found; Grep over research*.md found 0) + +INHERITED-PORCELAIN: +?? .claude/agent-memory/atomic-planner/project_964_partial_split_sink_guard_plan_seams.md +?? .claude/agent-memory/atomic-planner/project_964_r2_preparation_record_closed_evidence_set.md +?? .claude/agent-memory/atomic-planner/project_964_r3_glob_backslash_and_hit_attribution_seams.md +?? .claude/agent-memory/orchestrator/preparation-clearance-record-breaks-closed-evidence-set.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/phase0-instructions-read.md +?? docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md + +Write Set code path (verbatim): TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs + +Constraints of the remediation inputs (verbatim): +- Test-only change set: `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` only; no production file, csproj or other test file changes. The SinkGuard partial must stay at or below 500 lines. +- MSTest, Moq and FluentAssertions; no temporary files; no `Thread.Sleep` / `Task.Delay`; deterministic. +- Full CLAUDE.md C# toolchain in order (csharpier format and check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild`, `Invoke-MSTestWithCoverage.ps1`) with numeric coverage evidence, restart from step 1 on any failure or file change. +- Evidence under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence//` only. +- Do not weaken or edit any acceptance criterion in `issue.md`. + +Plan of record: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/sinkguard-partial-baseline.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/sinkguard-partial-baseline.md new file mode 100644 index 000000000..cc09793a7 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/remediation-baseline/sinkguard-partial-baseline.md @@ -0,0 +1,37 @@ +# SinkGuard partial baseline (P0-T3) + +Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read) +Command: Grep tool counts over TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs for the TOKENS-R2, TOKENS-R1 and TOKENS-FORBIDDEN patterns and `^`; Grep -o `public async Task \w+`; Grep counts over TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests*.cs, EngineToggleStateCoordinatorTests.cs and TaskMaster.Test/TaskMaster.Test.csproj. +EXIT_CODE: 0 +Output Summary: every count equals its false-before value; SinkGuard 169 lines; four existing test names; harness members present once each; primary fixture 481 lines; csproj registration 1. + +SINKGUARD-LINES-BEFORE: 169 + +TOKENS-R2 (before): +- `harness\.Engines\.VerifyNoOtherCalls\(\);` = 1 +- `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` = 1 +- `Engines\.VerifyNoOtherCalls` = 1 + +TOKENS-R1 (before): +- `\[DataTestMethod\]` = 0 +- `\[DataRow\(null\)\]` = 0 +- `\[DataRow\(""\)\]` = 0 +- R1-NAME = 0 +- `\[TestMethod\]` = 4 +- `#region ` = 2 +- `#endregion ` = 2 + +TOKENS-FORBIDDEN (combined alternation, one pattern): `Thread\.Sleep`, `Task\.Delay`, `DoNotParallelize`, `GetTempPath`, `File\.`, `DateTime\.Now`, `DateTime\.UtcNow` = 0 matching lines (each 0). +Positive control: `TaskCompletionSource` = 2. + +SINKGUARD-NAMES-BEFORE: +- HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow (line 31) +- HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing (line 52) +- HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow (line 88) +- GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain (line 126) + +R1-NAME count over the seven partials (EngineToggleStateCoordinatorTests*.cs): 0 matching files. + +Harness (primary fixture): `internal Mock Engines` 1 (line 427), `internal List Notifications` 1 (460), `internal List Errors` 1 (462), `internal List Invalidations` 1 (458), `internal Action OnNotify` 1 (456), `\[DataRow\(null\)\]` 1 (line 102). Primary fixture `^` count: 481. + +TEST-CSPROJ-REGISTRATION: 1 diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md new file mode 100644 index 000000000..44503a8f3 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md @@ -0,0 +1,442 @@ +# 2026-10-01-engine-toggle-coordinator-947-review-residuals (Remediation Plan, cycle 1) + +- **Issue:** #964 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Work Mode:** minor-audit (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` line 12 reads `- Work Mode: minor-audit`) +- **Last Updated:** 2026-10-03T08-43 +- **Status:** Authored, awaiting preflight +- **Version:** 1.0 (initial authoring of remediation cycle 1) +- **Task counts (mechanical):** Phase 0 has 9 tasks (P0-T1 to P0-T9), Phase 1 has 8 (P1-T1 to P1-T8), Phase 2 has 16 (P2-T1 to P2-T16); 33 in total. +- **Plan path continuity:** this file is updated in place for every revision round. No timestamped sibling plan file is created for this cycle. The executed plan `plan.2026-10-02T05-20.md` is read-only context and is not edited. +- **Execution topology:** The executor for this plan must be dispatched without worktree isolation: the Bash-tool isolation guard refuses every `pwsh` invocation in an isolated agent, and every toolchain step in this plan is a pwsh payload. + +**Fail-closed evidence rule:** every command-bearing task writes one evidence artifact carrying `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`. A task whose artifact is missing or incomplete stays unchecked, and the plan outcome is BLOCKED or INCOMPLETE, never PASS. Baseline, final-QC and coverage-comparison artifacts are mandatory; a missing one makes the audit verdict BLOCKED. A task that uses only the Edit, Read or Grep tools writes the same four fields, with `Command:` naming the tools and the patterns used and `EXIT_CODE: 0` when every acceptance clause of the task holds. + +**Evidence accounting rule:** the artifact path is named in each task. Do not mark an evidence-bearing task complete without the artifact on disk at that exact path. + +**Evidence location:** every artifact lives under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/` in the canonical sub-kinds `remediation-baseline/`, `regression-testing/`, `qa-gates/` and `other/`. EVIDENCE_LOCATION_OVERRIDE_REJECTED: none supplied. In task text the token FEATURE abbreviates `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964`, and the token CYCLE-BASE abbreviates the commit `6b8e935c177128d2f455f7bcd2fedc7deff6e30f`. + +## Requirement sources + +- Primary input: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-inputs.2026-10-03T08-43.md`: finding R-1 (from CR-1), finding R-2 (from CR-4), and its `## Constraints` section, which binds this plan. +- Supporting context (read only): `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md` (CR-1, CR-4, O-1), `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` section `## Acceptance Criteria` (AC1 to AC8, all eight already checked `- [x]`; this plan edits none of them and does not weaken any), the executed plan `plan.2026-10-02T05-20.md` (command definitions reused below) and `evidence/qa-gates/coverage-final.md` (coverage figures and the class-node read-out method). +- Cycle base: commit `6b8e935c177128d2f455f7bcd2fedc7deff6e30f` (CYCLE-BASE), the commit that opened remediation cycle 1 (last entry of the worktree reflog). Every `git diff` in this plan that measures the cycle uses that literal as its ref operand. The prior-cycle anchor `94287369908cc920b21b0e3256314f988ad7d2f5` (BASE-SHA of the executed plan) is used only as the ref of one negative control in P2-T8. +- No `spec.md`, `user-story.md` or `research.md` exists in the feature folder or is required; P0-T2 fails closed if one appears. + +## AC identity table + +| ID | Subject | Proved by | +|---|---|---| +| R-1 | Null and empty engine key on the refusal path is tested; the Messages partial class node reaches branch-rate 1 | `HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing` (two data rows) passing at P1-T6; class-node read-out at P2-T5 | +| R-2 | The both-sinks-throw test asserts `Engines.VerifyNoOtherCalls()` and an empty `Invalidations` | P1-T1 token counts; `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` passing at P1-T6; P1-T8 numstat shows zero deleted lines | +| CONSTRAINTS | Test-only change set, SinkGuard partial at most 500 lines, MSTest, Moq and FluentAssertions, no temp files and no sleeps, evidence under FEATURE/evidence only, no acceptance criterion edited | P1-T4 token gates, P1-T8 and P2-T8 footprint gates, P2-T9 line counts | +| AC8 | Full CLAUDE.md C# toolchain in one clean pass with coverage not lower | Phase 2 loop P2-T1 to P2-T7 | + +## Verified tree facts (re-derived in this worktree for version 1.0; Phase 0 re-checks each one) + +1. The worktree `.git` file names the gitdir of this worktree, whose `HEAD` is `ref: refs/heads/bug/engine-toggle-coordinator-947-review-residuals-964`; the last reflog entry of that gitdir moves HEAD to `6b8e935c177128d2f455f7bcd2fedc7deff6e30f` (`docs(964): open remediation cycle 1 for related review findings CR-1 and CR-4`). +2. `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` is 169 lines. Usings at 1 to 5 (`System`, `System.Threading.Tasks`, `FluentAssertions`, `Microsoft.VisualStudio.TestTools.UnitTesting`, `Moq`); namespace `TaskMaster.Test.Ribbon` at 7; `public partial class EngineToggleStateCoordinatorTests` at 20; `#region Issue #964 — a throwing notification sink on the refusal path` at 22; three tests in it: `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` (31), `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing` (52) and `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` (88, body 89 to 112); the matching `#endregion` at 114; blank 115; `#region Issue #964 — the issue #948 record placement under the shared guard` at 116; `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` at 126; that region's `#endregion` at 167; class and namespace close at 168 and 169. The sibling test at 52 to 79 ends with `harness.Engines.VerifyNoOtherCalls();` (77) and `harness.Invalidations.Should().BeEmpty("a refused click changes no state to display");` (78). The both-sinks test ends with the `Errors[0].Exception.Should().BeSameAs(` chain at 105 to 111, whose last two lines are `"the log sink receives the notification failure unchanged"` (110, 20 spaces) and `);` (111, 16 spaces), then `}` at 112. +3. `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` is 481 lines (O-1 of the code review; it is not edited by this plan). Namespace `TaskMaster.Test.Ribbon` at 9, `[TestClass]` at 22, `SpamEngine` constant at 25. Existing data-driven precedent at 101 to 107: `[DataTestMethod]`, `[DataRow(null)]`, `[DataRow("")]`, `[DataRow(" ")]` on a `string engineName` parameter. `private sealed class Harness` at 403; its members used by this plan: `internal Mock Engines { get; } =` (427, strict mock), `Coordinator` (430), `EnginesAvailable` (436, default true), `OnNotify` (456), `Invalidations` (458), `Notifications` (460), `Errors` (462). The notification sink records into `Notifications` and then invokes `OnNotify?.Invoke(message);` (414 to 418). `HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing` (355 to 372) already covers the non-null key with unavailable engines. +4. `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` is 86 lines. `private const string NullEngineNameToken = "(null)";` at 12; `RenderEngineName` at 17 to 20 with `return string.IsNullOrEmpty(engineName) ? NullEngineNameToken : engineName;` at 19 (the code review cites this arm as line 20; the file read in this pass places the ternary at line 19 and the closing brace at 20); `BuildUnavailableMessage` at 25 to 33 passes `RenderEngineName(engineName)` at 31 and renders `"The engine '{0}' is not available yet, ..."` with `CultureInfo.CurrentCulture`. The refusal path of `HandleToggleClickAsync` calls `BuildUnavailableMessage(engineName)`, so a null or empty key yields the text `The engine '(null)' is not available yet, ...`. This file is production code and is not edited by this plan. +5. `TaskMaster.Test/TaskMaster.Test.csproj` already registers `Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs` (code review: line 364); no csproj edit is needed because the new test lives in an existing partial. +6. `TaskMaster.Test/packages.config`: `MSTest.TestAdapter` and `MSTest.TestFramework` 4.4.1 (43, 44), `FluentAssertions` 8.11.0 (7). Fixture census at CYCLE-BASE: 44 attribute lines of `[TestMethod]`, `[DataTestMethod]` and `[DataRow(` in the seven partials (40, 1 and 3), so 43 executed cases (the first-cycle pass-after run observed 43; 40 `[TestMethod]` plus 3 data rows, a data-driven method reporting one counter per row and no parent counter). +7. `evidence/qa-gates/coverage-final.md` (first-cycle final run): `COORD-FILE` rows for the main, Prime and Messages files read `nodes=1` with covered=valid of 89, 72 and 42 (line 89 to 91); `COORD-LINES covered=203 valid=203` (93); `COORD-BRANCHES covered=43 valid=44` (94); the summary reads `Total 7388, executed 7388, passed 7388, failed 0.` (37); the `First-party coverage:` line reads `lines 56629/65881 (85.96%), branches 13683/17082 (80.10%)` (13). The raw document `coverage/final-964.cobertura.xml` is still on disk (git-ignored): its Messages class node at line 230924 reads ``, and the node's single method `RenderEngineName` reads `branch-rate="0.5"`. So the type aggregate 43/44 is 42/42 in the main and Prime files plus 1/2 in the Messages file, and the arm that closes the gap is the one at fact 4. A passing new test adds no production line or branch, so the expected final figures are `COORD-LINES covered=203 valid=203` and `COORD-BRANCHES covered=44 valid=44`. +8. `evidence/qa-gates/csharpier-check-final.md`: `Checked 1640 files in 5276ms.` and exit 0 on the first-cycle final tree. `.csharpierignore` excludes `**/evidence/**` (the feature folder is never formatted). +9. `scripts/vscode/Invoke-MSTestWithCoverage.ps1` and its helpers are unchanged since the executed plan: the runner prints `First-party coverage:` on success, writes the JaCoCo projection and the trx summary, retains the post-processed Cobertura document at `coverage\coverage.cobertura.xml`, and throws `MSTest with coverage failed with exit code N` on a non-zero collector exit before post-processing (executed plan, fact 8). The helper `Get-CoberturaClassLineSummary -ClassNode` returns `CoveredLines`, `TotalLines`, `CoveredBranches` and `TotalBranches` (the executed plan's `CMD-COVERAGE-POST` summed these into the observed `COORD-BRANCHES covered=43 valid=44`). +10. Hook behaviour that constrains payload wording (delegation prompt): `enforce-promotion-mcp-only.ps1` blocks a pwsh payload that contains the case-insensitive substrings `gh`, `issue` and `new` together. No payload in this plan contains the substring `issue`; `CMD-REBUILD`, `CMD-COVERAGE-POST` and `CMD-VSTEST` contain `new` (through `New-Object` and `New-Item`) and no `issue`, so the combination is absent. Where a payload cannot avoid one of the tokens the plan keeps the command and a hook denial is reported verbatim and stops that task (D-6). + +## Design decisions (do not redesign) + +- **D-1 Test-only remediation.** The only code file this cycle changes is `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`. No production file, no csproj, no other test file and no `issue.md` line changes. The primary fixture stays at 481 lines (O-1). +- **D-2 R-1 test shape.** One data-driven test, `[DataTestMethod]` with `[DataRow(null)]` and `[DataRow("")]` on a `string engineName` parameter (the same attribute spelling the primary fixture uses at fact 3), in a new region of the SinkGuard partial. It reuses the `Harness` with `EnginesAvailable = false`, calls `HandleToggleClickAsync(engineName)`, and asserts: no throw, exactly one notification whose text contains `(null)`, no error logged, `Engines.VerifyNoOtherCalls()` and no invalidation. It uses the same Arrange-Act-Assert layout, because-reason style and `Func` plus `NotThrowAsync` form as the existing refusal-path tests. +- **D-3 R-2 edit shape.** The both-sinks-throw test gains the same two statements its sibling carries (fact 2), appended after its last assertion: `harness.Engines.VerifyNoOtherCalls();` and `harness.Invalidations.Should().BeEmpty("a refused click changes no state to display");`. No existing line changes, so the numstat of the whole cycle shows zero deleted lines in the file (P1-T8). +- **D-4 No fail-before run.** Both changes add assertions or coverage over behaviour that is already correct, so a failing run against unmodified production code is structurally impossible. The auditable substitute is a fail-before exception dossier (P1-T7) whose alternative proof is the false-before and true-after pair: the new test name has 0 hits before the edit (P0-T3) and the Messages class node reads branch-rate 0.5 before and 1 after (P0-T4, P2-T5). +- **D-5 Coverage route.** Step 4 runs `scripts/vscode/Invoke-MSTestWithCoverage.ps1` verbatim through `CMD-COVERAGE-RUNNER` with the stage label fixed to `remediation`, so the raw documents land at `coverage\remediation-964.cobertura.xml` and `coverage\remediation-964.trx` and do not overwrite `coverage\final-964.cobertura.xml`, which P2-T5 uses as the false-before control. The test step passes only when the runner exits 0 with `FAILED-FQN-COUNT: 0`: the first-cycle final run had no failing test (7388 of 7388), the change adds only passing tests, so a failure is a new defect and stops the plan. `CMD-COVERAGE-POST` here omits the raw-document conversion branch (a completed runner has already post-processed the document in place) and omits the per-method loop (no production method changes in this cycle); it adds per-file branch totals and the per-class `line-rate` and `branch-rate` attributes, which are the quantities R-1 targets. Every added row is cross-checked in P2-T5 by a Grep read of the same class node in the raw document, a format already observed at fact 7. +- **D-6 Edit route and hooks.** Every `.cs` change is made with the Edit tool, never through a shell write, so the repository hooks see it. A hook denial of any Edit, Write or shell command is reported verbatim and stops that task; the executor does not retry with a rephrased command or another tool. Edit anchors are chosen to be unique in the file; if the Edit tool reports a missing or non-unique match, the executor re-reads the region and repeats with the text actually present, which is not a hook denial. +- **D-7 Commit and push (final tasks).** The plan ends with staging by explicit path (never `git add -A`, because a queued sibling's untracked files or ambient `.claude/agent-memory/` files would be swept in), a commit and a push of the branch. `.claude/agent-memory/` paths are ambient state of other sessions, are never staged, and are admitted by every footprint gate. The commit-record artifact is written before staging so that it is part of the commit; the commit hash and push output are post-commit observations reported in the executor's return and are not written into a file that would reopen the tree. +- **D-8 Restart semantics.** If P2-T1 rewrites the SinkGuard partial (its hash changes) the loop restarts once from P2-T1 and the artifacts record `PASS-2:` sections; a second rewrite is `FORMAT NOT STABLE`: stop. Any other failure of P2-T1 to P2-T5, or any file change by a step after P2-T1, stops the plan with its artifact and returns to the orchestrator as a further remediation round, which re-enters at P2-T1 after the fix (restart from step 1). + +## Write Set (every file this plan creates or modifies) + +Code files (the only path outside the feature folder this plan may change): + +- `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (modify: one appended pair of assertions, one new region) + +Feature documents: + +- `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md` (task check-off edits only) + +Evidence files (fixed names; the write time is the `Timestamp:` field), all under FEATURE/evidence/: + +- `remediation-baseline/`: `phase0-instructions-read.md`, `scope-and-anchor.md`, `sinkguard-partial-baseline.md`, `coverage-baseline.md`, `bootstrap-probe.md`, `csharpier-check-baseline.md`, `msbuild-analyzer-baseline.md`, `msbuild-nullable-baseline.md`, `coordinator-tests-baseline.md` (nine) +- `regression-testing/`: `cycle1-r2-edit.md`, `cycle1-r1-edit.md`, `cycle1-format.md`, `cycle1-token-gates.md`, `cycle1-build.md`, `cycle1-fixture-run.md`, `fail-before-exception..md`, `cycle1-sinkguard-diff.md` (eight; the dossier name carries the host-clock time of its write) +- `qa-gates/`: `cycle1-csharpier-format.md`, `cycle1-csharpier-check.md`, `cycle1-msbuild-analyzer.md`, `cycle1-msbuild-nullable.md`, `cycle1-coverage.md`, `cycle1-coverage-comparison.md`, `cycle1-toolchain-pass.md`, `cycle1-footprint.md`, `cycle1-line-counts.md`, `cycle1-evidence-hygiene.md` (ten) +- `other/`: `cycle1-finding-closure.md`, `cycle1-reduced-audit-handoff.md`, `cycle1-commit-record.md` (three) +- Preparation-phase records (committed by the coordinator before execution; not written, modified or deleted by any task of this plan): any file `other/preflight-clearance..md`. + +Files this plan must not touch: every other file under `TaskMaster/` and `TaskMaster.Test/` (in particular `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, `.Prime.cs`, `.Messages.cs`, both csproj files and the partials `EngineToggleStateCoordinatorTests.cs`, `.Race.cs`, `.PrimeFaultOrdering.cs`, `.PrimeRegistration.cs`, `.ThrowingSink.cs`, `.RepeatFaultSuppression.cs`), `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`, the executed plan, `docs/features/potential/`, every `packages.config`, every file under `scripts/`, `.claude/rules/`, `config/` and `artifacts/`, `TaskMaster.runsettings`, `coverage.config`, `.editorconfig`, `.gitignore` and `.csharpierignore`. No raw trx, raw Cobertura document or msbuild log is copied into the feature folder under any name; raw documents stay under the git-ignored `coverage/` directory. + +## Delivered source (the executor writes these texts; CSharpier output wins on layout, and every gate reads wrap-tolerant tokens) + +Indentation rule: every block below is shown with four leading spaces of Markdown indent on each line; remove exactly those four spaces on every line when writing. The line numbers cited are those of the SinkGuard partial at CYCLE-BASE (fact 2). + +**E1 — R-2, `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` (Edit tool).** Replace the three-line text `OLD-E1` with `NEW-E1`. `OLD-E1` occurs exactly once in the file: the same `BeSameAs` tail inside the sibling test is followed by the `harness.Errors[0].Message` line, not by a closing brace. + +OLD-E1: + + "the log sink receives the notification failure unchanged" + ); + } + +NEW-E1: + + "the log sink receives the notification failure unchanged" + ); + harness.Engines.VerifyNoOtherCalls(); + harness.Invalidations.Should().BeEmpty("a refused click changes no state to display"); + } + +**E2 — R-1, new region (Edit tool).** Replace the single line `OLD-E2` (it occurs exactly once; the `#endregion` line of the same region begins `#endregion`, which does not contain the text `#region`) with `NEW-E2`, which is the new region, one blank line and the original line. + +OLD-E2: + + #region Issue #964 — the issue #948 record placement under the shared guard + +NEW-E2: + + #region Issue #964 — the refusal path with a null or empty engine key + + /// + /// Refusal path for an unusable engine key: with the engines unavailable, a null or empty + /// key is rendered as the (null) token in the one notification, the click does not + /// throw, nothing is logged, no engine member is invoked and no control is invalidated. + /// Exercises the null-or-empty arm of the engine-name renderer through the notification + /// message builder. + /// + [DataTestMethod] + [DataRow(null)] + [DataRow("")] + public async Task HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing( + string engineName + ) + { + // Arrange: the pre-SetGlobals window, with sinks that record and do not throw. + var harness = new Harness { EnginesAvailable = false }; + + // Act + Func act = () => harness.Coordinator.HandleToggleClickAsync(engineName); + + // Assert + await act.Should() + .NotThrowAsync("a refused click with an unusable key must degrade quietly"); + harness + .Notifications.Should() + .ContainSingle("exactly one notice per refused toggle click"); + harness + .Notifications[0].Should() + .Contain("(null)", "an unusable key is rendered as the null-engine-name token"); + harness.Errors.Should().BeEmpty("a refused click is not a fault"); + harness.Engines.VerifyNoOtherCalls(); + harness.Invalidations.Should().BeEmpty("a refused click changes no state to display"); + } + + #endregion Issue #964 — the refusal path with a null or empty engine key + + #region Issue #964 — the issue #948 record placement under the shared guard + +Expected size after both edits and the format: 169 plus 2 plus 38, about 209 lines (an observation; the gates are the bounds in P1-T4 and P2-T9). + +## Name lists + +- `R1-NAME`: `HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing` (a data-driven method that reports two results, one per row). +- `SINKGUARD-NAMES` (existing, all four): `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow`, `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing`, `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` (this one is `R2-NAME`), `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain`. +- `INVARIANT-NAMES` (existing, eleven): `GetPressed_WhenPrimeStarts_RegistersPrimeHandleBeforeActivationReadRuns`, `GetPressed_AfterPrimeFaultsSynchronously_LaterReadStartsNewPrime`, `GetPressed_AfterPrimeIsCanceledSynchronously_LaterReadStartsNewPrime`, `GetPressed_WhenPrimeFaults_PrimeHandleStaysRegisteredUntilFaultIsLogged`, `GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime`, `GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime`, `GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared`, `HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport`, `GetPressed_WhenPrimeFaultsRepeatedly_LogsFirstFaultOnly`, `GetPressed_WhenPrimeIsCanceledRepeatedly_LogsFirstCancellationOnly`, `HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing`. +- `NAMES-CYCLE` is `R1-NAME` followed by `SINKGUARD-NAMES` and `INVARIANT-NAMES`, written as a PowerShell list of double-quoted strings when substituted into `CMD-VSTEST`. +- Expected fixture totals: `BASELINE-TOTAL: 43` before the edits and `FIXTURE-TOTAL: 45` after them (43 plus the two data rows). + +## Execution conventions + +- **Paths.** `WORKTREE` denotes the absolute item worktree path from the delegation prompt; it is substituted into each payload's first line and is never written into an artifact. Every artifact records repository-relative paths; absolute paths in transcribed tool output are replaced by `REDACTED-PATH`. +- **Payload channel.** Each indented payload block is executed as one `pwsh -NoProfile -Command ''` Bash invocation (the only permitted shell form besides `git`): outer single quotes, inner double quotes only; a double quote needed inside a payload string is built from `[char]34` and an apostrophe from `[char]39`; no payload carries a backslash-escaped double quote, a literal apostrophe or a backtick. No payload combines git with the substrings add, commit or remove, and no payload contains all of gh, pr and create, because the hook command scanners match those by case-insensitive containment. No payload contains the substring `issue` (fact 10). The `Command:` field of an artifact records the canonical command the payload runs, not the payload text. +- **Git commands outside payloads** run as `git -C WORKTREE ` (one command per Bash invocation, no chaining); the `Command:` field records them without `-C`. +- **Toolchain commands.** Every `dotnet`, `msbuild` and `vstest.console.exe` command named in a task runs inside one `pwsh -NoProfile -Command` payload that begins with PRELUDE, because the first token of a permitted Bash command must be `git`, `pwsh` or `poetry`. A task that names only the tool command (for example `dotnet tool run csharpier check .`) means that command wrapped as `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; ; "EXIT: $LASTEXITCODE"'`. +- **Grep tool patterns.** Every pattern this plan gives to the Grep tool is a regular expression with its metacharacters escaped as written: `\(`, `\)`, `\[`, `\]`, `\.`, `\?`, and a literal backslash as `\x5C`. A pattern written without any metacharacter is a plain phrase. The Grep tool counts matching lines. A Grep over a git-ignored file names that file as the path (an explicit file path is read; a directory path is filtered by `.gitignore`). +- **PRELUDE** (the first two lines of every payload): + + Set-Location -LiteralPath "WORKTREE" + [Environment]::CurrentDirectory = (Get-Location).Path + +- **TOOLS** (the lines of every build and test payload after PRELUDE): + + $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" + $msbuild = & $vswhere -latest -products * -find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1 + $vstest = & $vswhere -latest -products * -find "Common7\IDE\Extensions\TestPlatform\vstest.console.exe" | Select-Object -First 1 + $sln = Join-Path (Get-Location).Path "TaskMaster.sln" + New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null + +- **Exit codes.** `EXIT_CODE:` records the printed exit value of the principal command. A deliberately or admissibly failing run carries `ExpectedExitCode:` equal to the observed non-zero value in its own artifact; no task of this plan expects a non-zero exit. +- **Long runs.** `CMD-COVERAGE-RUNNER` is started as a background process with output redirected to `coverage\logs\remediation-964.payload.log`; completion is detected by the final line `PAYLOAD-COMPLETE`. Before every vstest or coverage run the executor runs `pwsh -NoProfile -Command '"STRAY_TEST_PROCESSES: " + @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -like "vstest*" -or $_.ProcessName -like "testhost*" -or $_.ProcessName -like "dotnet-coverage*" }).Count'` and proceeds only on `STRAY_TEST_PROCESSES: 0`. A coverage run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report; it is never re-run with a different route. +- **Stop rule.** A named stop condition halts the plan; the executor reports the artifact and the failing values and does not work around it. + +## Command reference + +**CMD-REBUILD** (`GATEARGS` is `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` or `/p:TreatWarningsAsErrors=true`; `TASKID` substituted; `Command:` records `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS` resolved through vswhere with the worktree solution by absolute path; never `/t:Build`, never `/p:Nullable=enable`): + + PRELUDE + TOOLS + $log = "coverage\logs\TASKID.msbuild.log" + if (Test-Path -LiteralPath $log) { Remove-Item -LiteralPath $log -Force } + $global:LASTEXITCODE = 0 + & $msbuild $sln /t:Rebuild /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS "/flp:LogFile=$log;Verbosity=normal" | Out-Null + Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE) + $lines = Get-Content -LiteralPath $log -Encoding UTF8 + Write-Output ("ERRORS: " + (($lines | Select-String -Pattern "^\s*(\d+) Error\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + Write-Output ("WARNINGS: " + (($lines | Select-String -Pattern "^\s*(\d+) Warning\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + Write-Output ("CSC_OUT_TASKMASTER: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.dll") }).Count) + Write-Output ("CSC_OUT_TASKMASTER_TEST: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.Test.dll") }).Count) + Write-Output ("WRITESET_DIAGNOSTIC_LINES: " + @($lines | Where-Object { ($_.Contains("EngineToggleStateCoordinatorTests.SinkGuard")) -and ($_ -match "(error|warning) [A-Z]+\d+") }).Count) + Write-Output ("COORDINATOR_DIAGNOSTIC_LINES: " + @($lines | Where-Object { ($_.Contains("EngineToggleStateCoordinator")) -and ($_ -match "(error|warning) [A-Z]+\d+") }).Count) + Write-Output ("TEST_DLL_EXISTS: " + (Test-Path -LiteralPath "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll")) + +`ERRORS:` is read from the summary line, so `0 Error(s)` is never matched inside a larger count. The two `CSC_OUT_` counts are the observation that the compiler ran for the two projects. `WRITESET_DIAGNOSTIC_LINES` counts every error or warning line naming the one Write Set source file; `COORDINATOR_DIAGNOSTIC_LINES` counts every error or warning line naming any coordinator production or fixture file (the executed plan's `WRITESET_DIAGNOSTIC_LINES` definition), so a diagnostic in a file this cycle does not touch is still visible. + +**CMD-BUILD** (incremental build so a scoped test run observes a fresh assembly; `TASKID` substituted; `Command:` records `msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU"`; never used as a toolchain gate): + + PRELUDE + TOOLS + $log = "coverage\logs\TASKID.msbuild.log" + if (Test-Path -LiteralPath $log) { Remove-Item -LiteralPath $log -Force } + $before = (Get-Item -LiteralPath "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll" -ErrorAction SilentlyContinue).LastWriteTimeUtc + $global:LASTEXITCODE = 0 + & $msbuild $sln /t:Build /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" "/flp:LogFile=$log;Verbosity=normal" | Out-Null + Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE) + $lines = Get-Content -LiteralPath $log -Encoding UTF8 + Write-Output ("ERRORS: " + (($lines | Select-String -Pattern "^\s*(\d+) Error\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + $after = (Get-Item -LiteralPath "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll").LastWriteTimeUtc + Write-Output ("TEST_DLL_ADVANCED: " + ($null -eq $before -or $after -gt $before)) + Write-Output ("CSC_OUT_TASKMASTER: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.dll") }).Count) + Write-Output ("CSC_OUT_TASKMASTER_TEST: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\TaskMaster.Test.dll") }).Count) + +**CMD-VSTEST** (coordinator fixture run; `TASKID` and `NAMES` substituted; `Command:` records `vstest.console.exe TaskMaster.Test\bin\Debug\TaskMaster.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:coverage\test-results\964\TASKID" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"` resolved through vswhere; a run whose filter matches zero tests is a failure). The `RESULT` line is the executed plan's line adapted for data-driven methods: it counts every result whose `testName` equals the method name or begins with the method name followed by ` (`, which is how a data row is named: + + PRELUDE + TOOLS + $results = "coverage\test-results\964\TASKID" + if (Test-Path -LiteralPath $results) { Remove-Item -LiteralPath $results -Recurse -Force } + $names = @(NAMES) + $global:LASTEXITCODE = 0 + & $vstest "TaskMaster.Test\bin\Debug\TaskMaster.Test.dll" /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FullyQualifiedName~TaskMaster.Test.Ribbon.EngineToggleStateCoordinatorTests" "/ResultsDirectory:$results" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" 2>&1 | Tee-Object -FilePath "coverage\logs\TASKID.vstest.log" | Out-Null + Write-Output ("VSTEST_EXIT_CODE: " + $LASTEXITCODE) + $trxPath = Join-Path $results "TASKID.trx" + Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath $trxPath)) + Write-Output ("SEQUENCE_FILES: " + @(Get-ChildItem -LiteralPath $results -Recurse -Filter "Sequence_*.xml" -ErrorAction SilentlyContinue).Count) + if (-not (Test-Path -LiteralPath $trxPath)) { exit 3 } + [xml]$trx = Get-Content -LiteralPath $trxPath -Raw -Encoding UTF8 + $ns = New-Object System.Xml.XmlNamespaceManager($trx.NameTable) + $ns.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010") + $counters = $trx.SelectSingleNode("//t:ResultSummary/t:Counters", $ns) + Write-Output ("COUNTERS total=" + $counters.GetAttribute("total") + " executed=" + $counters.GetAttribute("executed") + " passed=" + $counters.GetAttribute("passed") + " failed=" + $counters.GetAttribute("failed")) + $all = @($trx.SelectNodes("//t:UnitTestResult", $ns)) + foreach ($n in $names) { $rows = @($all | Where-Object { $_.GetAttribute("testName") -eq $n -or $_.GetAttribute("testName").StartsWith($n + " (") }); Write-Output ("RESULT " + $n + " rows=" + $rows.Count + " passed=" + @($rows | Where-Object { $_.GetAttribute("outcome") -eq "Passed" }).Count) } + foreach ($r in $all) { if ($r.GetAttribute("outcome") -eq "Failed") { Write-Output ("FAILED " + $r.GetAttribute("testName")); $msg = $r.SelectSingleNode("t:Output/t:ErrorInfo/t:Message", $ns); Write-Output ("MESSAGE " + $r.GetAttribute("testName") + " :: " + $(if ($msg) { $msg.InnerText.Replace([string][char]13, " ").Replace([string][char]10, " / ") } else { "(no message)" })) } } + +The artifact transcribes the `COUNTERS`, `RESULT`, `FAILED` and `MESSAGE` lines; the trx stays under the ignored coverage directory. + +**CMD-COVERAGE-RUNNER** (CLAUDE.md step 4, stage label `remediation`; `Command:` records `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1` invoked by absolute script path from the worktree directory): + + PRELUDE + New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null + foreach ($f in @("coverage\coverage.cobertura.xml", "coverage\coverage.cobertura.jacoco.xml", "coverage\test-results\mstest-coverage-run.trx", "coverage\test-results\mstest-coverage-run.summary.txt", "coverage\remediation-964.cobertura.xml", "coverage\remediation-964.trx")) { if (Test-Path -LiteralPath $f) { Remove-Item -LiteralPath $f -Force } } + $script = Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.ps1" + $global:LASTEXITCODE = 0 + & pwsh -NoProfile -File $script 2>&1 | Tee-Object -FilePath "coverage\logs\remediation-964.runner.log" | Out-Null + Write-Output ("RUNNER_EXIT_CODE: " + $LASTEXITCODE) + $log = Get-Content -LiteralPath "coverage\logs\remediation-964.runner.log" -Raw -Encoding UTF8 + Write-Output ("DISCOVERED_LINE: " + [regex]::Match($log, "Discovered \d+ test assemblies\.").Value) + Write-Output ("FIRST_PARTY_LINE: " + [regex]::Match($log, "First-party coverage: [^\r\n]*").Value) + Write-Output ("THRESHOLD_MESSAGE: " + [regex]::Match($log, "Cobertura (line|branch) coverage [^\r\n]*threshold[^\r\n]*").Value) + Write-Output ("COLLECT_FAILURE_MESSAGE: " + [regex]::Match($log, "MSTest with coverage failed with exit code \d+").Value) + Write-Output ("DOCUMENT_PRESENT: " + (Test-Path -LiteralPath "coverage\coverage.cobertura.xml")) + Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.trx")) + Write-Output ("SUMMARY_FILE_PRESENT: " + (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.summary.txt")) + if (Test-Path -LiteralPath "coverage\coverage.cobertura.xml") { Copy-Item -LiteralPath "coverage\coverage.cobertura.xml" -Destination "coverage\remediation-964.cobertura.xml" -Force } + if (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.trx") { Copy-Item -LiteralPath "coverage\test-results\mstest-coverage-run.trx" -Destination "coverage\remediation-964.trx" -Force } + Write-Output "PAYLOAD-COMPLETE" + +The stale-output removal makes every `_PRESENT` value an observation of this run. Lines of the runner log that carry absolute paths stay in the ignored log; only the named values are transcribed. + +**CMD-COVERAGE-POST** (run only after a runner exit of 0, so the document is already post-processed; adapted from the executed plan as described in D-5: no raw-conversion line, no per-method loop, and per-file branch totals plus per-class rate attributes added): + + PRELUDE + . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1") + . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTest.TrxSummary.ps1") + $ErrorActionPreference = "Continue" + $summary = Get-TrxRunSummary -TrxContent (Get-Content -LiteralPath "coverage\remediation-964.trx" -Raw -Encoding UTF8) + Write-Output "SUMMARY-BEGIN" + Write-Output (Format-TrxRunSummary -Summary $summary) + Write-Output "SUMMARY-END" + Write-Output ("FAILED-SET: " + (@($summary.FailedTestName) -join ", ")) + [xml]$trxXml = Get-Content -LiteralPath "coverage\remediation-964.trx" -Raw -Encoding UTF8 + $tns = New-Object System.Xml.XmlNamespaceManager($trxXml.NameTable) + $tns.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010") + $defs = @{} + foreach ($u in @($trxXml.SelectNodes("//t:TestDefinitions/t:UnitTest", $tns))) { $tm = $u.SelectSingleNode("t:TestMethod", $tns); $defs[$u.GetAttribute("id")] = $tm.GetAttribute("className").Split([char]44)[0].Trim() + "." + $tm.GetAttribute("name") } + Write-Output ("TEST-DEFINITIONS: " + $defs.Count) + $fqn = @(@($trxXml.SelectNodes("//t:Results/t:UnitTestResult", $tns)) | Where-Object { $_.GetAttribute("outcome") -eq "Failed" } | ForEach-Object { $id = $_.GetAttribute("testId"); if ($defs.ContainsKey($id)) { $defs[$id] } else { "UNRESOLVED:" + $id } } | Sort-Object -Unique -CaseSensitive) + Write-Output ("FAILED-FQN-COUNT: " + $fqn.Count) + foreach ($n in $fqn) { Write-Output ("FAILED-FQN " + $n) } + $doc = Get-Content -LiteralPath "coverage\remediation-964.cobertura.xml" -Raw -Encoding UTF8 + try { Assert-CoberturaLineCoverageThreshold -CoberturaXml $doc; Write-Output "LINE-FLOOR: MET" } catch { Write-Output ("LINE-FLOOR: NOT MET " + $_.Exception.Message) } + try { Assert-CoberturaBranchCoverageThreshold -CoberturaXml $doc; Write-Output "BRANCH-FLOOR: MET" } catch { Write-Output ("BRANCH-FLOOR: NOT MET " + $_.Exception.Message) } + Write-Output (Get-CoberturaFirstPartyCoverageReport -CoberturaXml $doc) + [xml]$xml = $doc + $root = $xml.SelectSingleNode("/coverage") + Write-Output ("ROOT line-rate=" + $root.GetAttribute("line-rate") + " branch-rate=" + $root.GetAttribute("branch-rate") + " lines-covered=" + $root.GetAttribute("lines-covered") + " lines-valid=" + $root.GetAttribute("lines-valid")) + $projection = ConvertTo-JacocoPackageProjection -XmlDocument $xml + Assert-JacocoProjectionReconciliation -XmlDocument $xml -ProjectionXml $projection + Write-Output "PROJECTION-BEGIN" + Write-Output $projection + Write-Output "PROJECTION-END" + $srcFiles = @("TaskMaster\Ribbon\EngineToggleStateCoordinator.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Prime.cs", "TaskMaster\Ribbon\EngineToggleStateCoordinator.Messages.cs") + $nodesTotal = 0; $cv = 0; $vl = 0; $cb = 0; $vb = 0 + foreach ($f in $srcFiles) { + $norm = $f.Replace([string][char]92, "/") + $nodes = @($xml.SelectNodes("//class[@filename]") | Where-Object { $_.GetAttribute("filename").Replace([string][char]92, "/").EndsWith($norm) }) + $fc = 0; $fv = 0; $fcb = 0; $fvb = 0 + foreach ($c in $nodes) { $s = Get-CoberturaClassLineSummary -ClassNode $c; $fc += $s.CoveredLines; $fv += $s.TotalLines; $fcb += $s.CoveredBranches; $fvb += $s.TotalBranches; Write-Output ("CLASS-NODE " + $norm + " line-rate=" + $c.GetAttribute("line-rate") + " branch-rate=" + $c.GetAttribute("branch-rate")) } + $nodesTotal += $nodes.Count; $cv += $fc; $vl += $fv; $cb += $fcb; $vb += $fvb + Write-Output ("COORD-FILE " + $norm + " nodes=" + $nodes.Count + " covered=" + $fc + " valid=" + $fv + " branches-covered=" + $fcb + " branches-valid=" + $fvb) + } + Write-Output ("COORD-CLASS-NODES: " + $nodesTotal) + Write-Output ("COORD-LINES covered=" + $cv + " valid=" + $vl) + Write-Output ("COORD-BRANCHES covered=" + $cb + " valid=" + $vb) + Write-Output ("COORD-LINE-RATE: " + $(if ($vl -gt 0) { [math]::Round(100.0 * $cv / $vl, 2) } else { "NA" })) + Write-Output ("COORD-BRANCH-RATE: " + $(if ($vb -gt 0) { [math]::Round(100.0 * $cb / $vb, 2) } else { "NA" })) + +The summary block and the projection are the two CLAUDE.md committed forms; every other line is a figure, not a document. `FAILED-SET:` (short method names, executed plan fact 14) is an observation only; the failing-test gate reads `TEST-DEFINITIONS:`, `FAILED-FQN-COUNT:` and the `FAILED-FQN` rows, which are fully qualified names. + +**CMD-LINECOUNT** (line counts of every fixture partial present, enumerated from the directory; adapted from the executed plan to the test partials, the only files this cycle may change): + + PRELUDE + $files = @(Get-ChildItem -LiteralPath "TaskMaster.Test\Ribbon" -File -Filter "EngineToggleStateCoordinatorTests*.cs" | Sort-Object Name | ForEach-Object { Join-Path "TaskMaster.Test\Ribbon" $_.Name }) + foreach ($p in $files) { Write-Output ("LINES " + $p + " = " + @(Get-Content -LiteralPath $p -Encoding UTF8).Count) } + Write-Output ("TEST-PARTIALS: " + $files.Count) + +**CMD-HASH-SINKGUARD** (SHA-256 of the one Write Set code file; a hash only): + + PRELUDE + $p = "TaskMaster.Test\Ribbon\EngineToggleStateCoordinatorTests.SinkGuard.cs" + if (Test-Path -LiteralPath $p) { Write-Output ("HASH " + $p + " = " + (Get-FileHash -Algorithm SHA256 -LiteralPath $p).Hash) } else { Write-Output ("HASH " + $p + " = ABSENT") } + +**CMD-HYGIENE** (host-identifier sweep over every Markdown file of the feature folder; the host tokens are derived at run time and never written into the artifact): + + PRELUDE + $acct = Split-Path -Leaf $env:USERPROFILE; $machine = $env:COMPUTERNAME + $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-10-01-engine-toggle-coordinator-947-review-residuals-964" -Recurse -File -Filter "*.md") + $a = 0; $m = 0; $d = 0 + foreach ($f in $files) { $c = Get-Content -LiteralPath $f.FullName -Raw -Encoding UTF8; $fa = ([regex]::Matches($c, [regex]::Escape($acct), "IgnoreCase")).Count; $fm = ([regex]::Matches($c, [regex]::Escape($machine), "IgnoreCase")).Count; $n = $c.Replace([string][char]92, "/"); $fd = ([regex]::Matches($n, "[a-z]:/+users/+[a-z0-9_.~-]", "IgnoreCase")).Count; $a += $fa; $m += $fm; $d += $fd; if (($fa + $fm + $fd) -gt 0) { Write-Output ("HIT-FILE " + $f.Directory.Name + "/" + $f.Name + " ACCOUNT=" + $fa + " MACHINE=" + $fm + " DRIVE_USERS=" + $fd) } } + $raw = @(Get-ChildItem -LiteralPath "docs\features\active\2026-10-01-engine-toggle-coordinator-947-review-residuals-964" -Recurse -File | Where-Object { $_.Name -like "*.trx" -or $_.Name -like "*cobertura*" -or $_.Name -like "*.coverage" -or $_.Name -like "*.coveragexml" -or $_.Name -like "*.log" }).Count + Write-Output ("FILES_SCANNED=" + $files.Count + " ACCOUNT_HITS=" + $a + " MACHINE_HITS=" + $m + " DRIVE_USERS_HITS=" + $d + " RAW_DOCUMENTS=" + $raw) + +## Token and size sets (quoted verbatim; each is the instruction the delivered source above fulfils) + +- `TOKENS-R2` (Grep counts over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`; false-before value, then required true-after value): `harness\.Engines\.VerifyNoOtherCalls\(\);` 1 then 3 (the sibling test, the both-sinks test after E1, and the new R-1 test after E2, which carries the same statement; 2 after E1 alone); `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` 1 then 3 (same three tests; 2 after E1 alone); `Engines\.VerifyNoOtherCalls` 1 then 3. +- `TOKENS-R1` (same file; false-before value, then required true-after value): `\[DataTestMethod\]` 0 then 1; `\[DataRow\(null\)\]` 0 then 1; `\[DataRow\(""\)\]` 0 then 1; `HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing` 0 then 1; `\[TestMethod\]` 4 then 4; `#region ` 2 then 3; `#endregion ` 2 then 3. +- `TOKENS-FORBIDDEN` (same file, required 0 before and after): `Thread\.Sleep`, `Task\.Delay`, `DoNotParallelize`, `GetTempPath`, `File\.`, `DateTime\.Now`, `DateTime\.UtcNow`. Positive control that the file was read: `TaskCompletionSource` at least 2. +- `SIZE-BOUNDS` (Grep count of pattern `^` over the SinkGuard partial): 169 before; after the edits and the format at least 195 and at most 500. + +### Phase 0 — Policy Reads, Anchor and Baseline Capture + +- [x] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/tonality.md and .claude/rules/plan-acceptance-gates.md, and record FEATURE/evidence/remediation-baseline/phase0-instructions-read.md. + - Acceptance: the artifact carries `Timestamp:`, a `Policy Order:` line naming CLAUDE.md, general-code-change.md, general-unit-test.md and csharp.md in that order, and a `Files read:` list naming all six repository-relative paths, one per line. No policy document is modified. +- [x] [P0-T2] Read FEATURE/remediation-inputs.2026-10-03T08-43.md in full, the `## Acceptance Criteria` section of FEATURE/issue.md and this plan, and record the requirements anchor and the Write Set in FEATURE/evidence/remediation-baseline/scope-and-anchor.md. + - Command: `git -C WORKTREE status --porcelain --untracked-files=all` (recorded verbatim as `INHERITED-PORCELAIN:`); `git -C WORKTREE merge-base 6b8e935c177128d2f455f7bcd2fedc7deff6e30f HEAD`; `git -C WORKTREE diff --exit-code --stat 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster TaskMaster.Test`; the Grep tool over `FEATURE/issue.md` with patterns `^- Work Mode: minor-audit`, `^## Acceptance Criteria`, `^- \[x\] AC[1-8] ` and `^- \[ \] AC`; the Glob tool over FEATURE for `spec.md`, `user-story.md` and `research*.md`. + - Acceptance, all required: the merge-base output equals `6b8e935c177128d2f455f7bcd2fedc7deff6e30f` (otherwise `CYCLE BASE NOT ANCESTOR`: stop; HEAD itself is not pinned, because a phase-boundary commit by the orchestrator legitimately advances it); the diff exits 0 and prints nothing (`ANCHOR-CODE-DIFF-EXIT=0`; otherwise `CODE DIFFERS FROM CYCLE BASE`: stop, because every later numstat gate assumes an unchanged code tree at the start); the issue Grep counts are 1, 1, 8 and 0 (the eight acceptance criteria stay checked and this plan edits none; any other count is `ACCEPTANCE SECTION CHANGED`: stop); the Glob result for the three names is `none` (a hit is `UNEXPECTED REQUIREMENTS DOCUMENT`: stop, per the minor-audit fail-closed rule); `INHERITED-PORCELAIN:` lists every entry, each of which must lie under FEATURE or under `.claude/agent-memory/` (any entry under `TaskMaster/` or `TaskMaster.Test/`, or any other path, is `UNEXPECTED INHERITED CHANGE`: stop); the artifact states the single Write Set code path verbatim, the constraint list of the remediation inputs verbatim, and the file `FEATURE/remediation-plan.2026-10-03T08-43.md` as this plan. +- [x] [P0-T3] Capture the baseline of the SinkGuard partial and its fixture harness in FEATURE/evidence/remediation-baseline/sinkguard-partial-baseline.md. + - Command: the Grep tool with pattern `^` (count) over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`; the Grep tool counts over that file for each pattern of `TOKENS-R2`, `TOKENS-R1` and `TOKENS-FORBIDDEN`; the Grep tool with `-o` and pattern `public async Task \w+` over that file (the current test names); the Grep tool counts of `HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing` over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests*.cs`; the Grep tool counts over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` for `internal Mock Engines`, `internal List Notifications`, `internal List Errors`, `internal List Invalidations`, `internal Action OnNotify`, `\[DataRow\(null\)\]` and the pattern `^` (count); the Grep tool count of `Ribbon\x5CEngineToggleStateCoordinatorTests\.SinkGuard\.cs"` over `TaskMaster.Test/TaskMaster.Test.csproj`. + - Acceptance, all required: the `^` count of the SinkGuard partial is 169, recorded as `SINKGUARD-LINES-BEFORE: 169`; every `TOKENS-R2`, `TOKENS-R1` and `TOKENS-FORBIDDEN` count equals its false-before value (`TOKENS-FORBIDDEN` 0 each, `TaskCompletionSource` at least 2); the `-o` listing names exactly the four `SINKGUARD-NAMES`, recorded as `SINKGUARD-NAMES-BEFORE:`; the `R1-NAME` count over the seven partials is 0 in every file; each harness count is 1, the primary fixture `^` count is 481 and the SinkGuard csproj registration count is 1 (recorded as `TEST-CSPROJ-REGISTRATION: 1`, which is why no csproj edit is planned). Any mismatch is `SINKGUARD ANCHOR MOVED`: stop. +- [x] [P0-T4] Record the coverage baseline of the coordinator files from the committed first-cycle final evidence in FEATURE/evidence/remediation-baseline/coverage-baseline.md. + - Command: the Grep tool with `-n` over `FEATURE/evidence/qa-gates/coverage-final.md` for the patterns `^COORD-BRANCHES covered=43 valid=44`, `^COORD-LINES covered=203 valid=203`, `^COORD-FILE TaskMaster/Ribbon/EngineToggleStateCoordinator\.Messages\.cs nodes=1 covered=42 valid=42`, `^- First-party coverage: lines ` and `^Total 7388, executed 7388, passed 7388, failed 0\.` (no pattern ends in a line anchor, because a CRLF line ending defeats `$` in the Grep tool); the Grep tool counts over `coverage/final-964.cobertura.xml` (an explicit file path) for the patterns `]*filename="TaskMaster\x5CRibbon\x5CEngineToggleStateCoordinator\.Messages\.cs">` and `]*filename="TaskMaster\x5CRibbon\x5CEngineToggleStateCoordinator\.Messages\.cs">`. + - Acceptance, all required: each of the five committed-evidence patterns has exactly one matching line, and the recorded values are `BASELINE-COORD-BRANCHES: 43/44` (97.73 percent), `BASELINE-COORD-LINES: 203/203`, `BASELINE-MESSAGES-LINES: 42/42`, `BASELINE-FIRST-PARTY:` (the `First-party coverage:` line as written in the artifact) and `BASELINE-TEST-TOTAL: 7388`; the raw-document read gives 1 for the `0\.5` pattern and 0 for the `1(\.0+)?` pattern, recorded as `BASELINE-MESSAGES-BRANCH-RATE: 0.5` (the class-node read-out and the false-before half of the P2-T5 gate). If the raw document is absent the artifact records `RAW-BASELINE-ABSENT` and `BASELINE-MESSAGES-BRANCH-RATE: not read locally, 0.5 as read by CR-1 of the code review`, and the plan continues, because the committed 43/44 is itself the numeric false-before value; any other value of a committed-evidence pattern is `COVERAGE BASELINE MOVED`: stop. +- [x] [P0-T5] Probe that the toolchain bootstrap of the worktree is in place and record FEATURE/evidence/remediation-baseline/bootstrap-probe.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version; dotnet tool list --local; "PACKAGE_DIRS=$(@(Get-ChildItem -LiteralPath packages -Directory -ErrorAction SilentlyContinue).Count)"; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"'` + - Acceptance, all required: `SDK_MARKER=True`; `dotnet --version` prints a version string rather than the global.json error message; the local tool list contains a row whose Package Id is `csharpier` and whose Version is `1.2.6` (only the Package Id and Version columns are transcribed, the Manifest column carries an absolute path); `PACKAGE_DIRS=` at least 1; `DOTNET_COVERAGE_RESOLVED=True`; `EXIT_CODE: 0`. Any other value is `BOOTSTRAP MISSING`: stop and report, because the first-cycle executor provisioned this worktree and a missing item means the tree changed underneath the plan. +- [x] [P0-T6] Capture the read-only formatter baseline over the worktree (`.csharpierignore` applies) with `dotnet tool run csharpier check .` and record FEATURE/evidence/remediation-baseline/csharpier-check-baseline.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'` + - Acceptance: `EXIT_CODE:` is the printed `CSHARPIER_EXIT_CODE:` value and is 0, the `Checked N files` line is recorded (the first-cycle value was `Checked 1640 files`; the figure is an observation), and every path CSharpier reports as unformatted is listed (none expected). A non-zero value is `FORMAT BASELINE NOT CLEAN`: stop, because the Phase 2 repository-wide format would then rewrite files outside the Write Set. +- [x] [P0-T7] Capture the analyzer baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p0-t7) and record FEATURE/evidence/remediation-baseline/msbuild-analyzer-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `COORDINATOR_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded as `ANALYZER-BASELINE-WARNINGS:`; `TEST_DLL_EXISTS: True`. A non-zero exit is `ANALYZER BASELINE NOT CLEAN`: stop. +- [x] [P0-T8] Capture the nullable baseline of TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p0-t8) and record FEATURE/evidence/remediation-baseline/msbuild-nullable-baseline.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `COORDINATOR_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded as `NULLABLE-BASELINE-WARNINGS:`; `TEST_DLL_EXISTS: True`. A non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop. +- [x] [P0-T9] Capture the pre-change coordinator fixture run over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll (assembly freshly rebuilt by P0-T8 from the unchanged tree) with `CMD-VSTEST` (`TASKID` p0-t9, `NAMES` `NAMES-CYCLE`) and record FEATURE/evidence/remediation-baseline/coordinator-tests-baseline.md. + - Acceptance, all required: `EXIT_CODE: 0` (the `VSTEST_EXIT_CODE:` value); `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `COUNTERS` with `total=43`, `passed=43` and `failed=0`, recorded as `BASELINE-TOTAL: 43`; every `SINKGUARD-NAMES` and `INVARIANT-NAMES` entry has a `RESULT rows=1 passed=1` line; the `R1-NAME` line reads `rows=0 passed=0` (false-before: the test does not exist yet); no `FAILED` line. Anything else is `EXISTING FIXTURE NOT GREEN AT CYCLE BASE`: stop. + +### Phase 1 — Constrained Implementation: R-2 Assertions, R-1 Refusal-Path Test, Then Targeted Verification + +Phase 1 is the constrained small-path implementation: test-only edits to one partial, then a build and a targeted run of the coordinator fixture. Ordering: R-2 first (one appended pair of statements), then R-1 (one new region), then format, token gates, build and fixture run, then the fail-before exception dossier and the diff-shape gate. + +- [x] [P1-T1] Apply delivered source E1 (R-2: the two appended assertions of `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`) to `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` with the Edit tool, and record FEATURE/evidence/regression-testing/cycle1-r2-edit.md. + - Acceptance, all required: the Grep tool counts over the file match the `TOKENS-R2` first-edit values `harness\.Engines\.VerifyNoOtherCalls\(\);` 2 and `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` 2; the Read tool over the file shows both new lines inside the body of `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`, after its `BeSameAs` chain and before that method's closing brace, and not inside any other method (recorded as `R2-PLACEMENT:` with the two line numbers read). A count of 1 for either pattern is `R-2 EDIT NOT APPLIED`; a count of 3 or more is `R-2 EDIT APPLIED TWICE`: correct with the Edit tool and re-check once, then stop. +- [x] [P1-T2] Apply delivered source E2 (R-1: the new region with the data-driven refusal-path test) to `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` with the Edit tool, and record FEATURE/evidence/regression-testing/cycle1-r1-edit.md. + - Acceptance, all required: the Grep tool counts over the file read `\[DataTestMethod\]` 1, `\[DataRow\(null\)\]` 1, `\[DataRow\(""\)\]` 1, `HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing` 1, `#region Issue #964 — the refusal path with a null or empty engine key` 1, `#endregion Issue #964 — the refusal path with a null or empty engine key` 1 and `harness\.Invalidations\.Should\(\)\.BeEmpty\("a refused click changes no state to display"\);` 3; the Read tool shows the new region between the `#endregion` of the first region and the `#region` of the issue-948 region, with the two `DataRow` lines directly above the method and the original issue-948 `#region` line still present once. Any other count is `R-1 EDIT MISAPPLIED`: correct with the Edit tool and re-check once, then stop. +- [x] [P1-T3] Format the SinkGuard partial with `dotnet tool run csharpier format TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`, then verify it with `dotnet tool run csharpier check TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (both wrapped per the Toolchain commands convention), and record FEATURE/evidence/regression-testing/cycle1-format.md. + - Acceptance, all required: the format exits 0 (its `Formatted N files` line is a processed count, not an assertion); the success-case observation is the check run, which exits 0, prints a line beginning `Checked 1 file` and lists no path. Both outputs are recorded under `FORMAT:`. A check run that lists the path is `FORMAT NOT CLEAN`: re-run the format and the check once, then stop. +- [x] [P1-T4] Verify the token, forbidden-token and size sets of the formatted SinkGuard partial (CONSTRAINTS, R-1, R-2) with the Grep tool and record FEATURE/evidence/regression-testing/cycle1-token-gates.md. + - Command: the Grep tool counts over `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` for every pattern of `TOKENS-R2`, `TOKENS-R1` and `TOKENS-FORBIDDEN`, and for the pattern `^`. + - Acceptance, all required: every `TOKENS-R2` and `TOKENS-R1` count equals its required true-after value (a formatter line break cannot change a count, because every pattern is a single-line token or a method name); every `TOKENS-FORBIDDEN` count is 0 and `TaskCompletionSource` is at least 2 (positive control that the file was read); the `^` count satisfies `SIZE-BOUNDS` (at least 195 and at most 500), recorded as `SINKGUARD-LINES-AFTER:`. Any mismatch is `TOKEN GATE FAILED`: stop. +- [x] [P1-T5] Build TaskMaster.sln with `CMD-BUILD` (`TASKID` p1-t5) so the fixture run observes the edited test assembly, and record FEATURE/evidence/regression-testing/cycle1-build.md. + - Acceptance, all required: `MSBUILD_EXIT_CODE: 0`; `ERRORS: 0`; `TEST_DLL_ADVANCED: True` (the test assembly was rebuilt after the edit; `False` means the run would observe a stale assembly and is `STALE TEST ASSEMBLY`: stop); `CSC_OUT_TASKMASTER_TEST:` at least 1. +- [x] [P1-T6] Run the coordinator fixture over TaskMaster.Test\bin\Debug\TaskMaster.Test.dll with `CMD-VSTEST` (`TASKID` p1-t6, `NAMES` `NAMES-CYCLE`) and record FEATURE/evidence/regression-testing/cycle1-fixture-run.md. + - Acceptance, all required: `EXIT_CODE: 0` (the `VSTEST_EXIT_CODE:` value); `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `COUNTERS` with `total=45`, `passed=45` and `failed=0`, recorded as `FIXTURE-TOTAL: 45` (the `BASELINE-TOTAL: 43` of P0-T9 plus the two data rows, so a run that did not discover the new rows reads 43 and fails this clause); the `R1-NAME` line reads `rows=` at least 2 with `passed=` equal to `rows=` (both data rows ran and passed); every `SINKGUARD-NAMES` entry, including `R2-NAME`, and every `INVARIANT-NAMES` entry reads `rows=1 passed=1`; no `FAILED` line. Anything else is `CYCLE FIXTURE NOT GREEN`: stop and report the `FAILED` and `MESSAGE` lines with absolute paths transcribed as `REDACTED-PATH`. +- [x] [P1-T7] Record the fail-before exception dossier for the cycle in FEATURE/evidence/regression-testing/fail-before-exception..md, the timestamp being the host-clock time of the write. + - Acceptance, all required: the artifact carries `Timestamp:`, `Command:` (the sources it cites), `EXIT_CODE: 0`, `Output Summary:`, a line `WhyFailingRunImpossible:` stating in one to three sentences that R-1 adds coverage over correct behaviour and R-2 adds assertions over correct behaviour, so no failing run against the unmodified production code exists, and an alternative-proof section citing, by artifact path and value, `R1-NAME` count 0 before the edit (`remediation-baseline/sinkguard-partial-baseline.md`), `BASELINE-MESSAGES-BRANCH-RATE: 0.5` and `BASELINE-COORD-BRANCHES: 43/44` (`remediation-baseline/coverage-baseline.md`), the `TOKENS-R2` and `TOKENS-R1` before and after counts (`remediation-baseline/sinkguard-partial-baseline.md` and `regression-testing/cycle1-token-gates.md`) and `FIXTURE-TOTAL: 45` against `BASELINE-TOTAL: 43` (`regression-testing/cycle1-fixture-run.md`). The final half of the proof (class-node branch-rate 1) is appended by P2-T6. +- [x] [P1-T8] Verify the diff shape of the cycle against CYCLE-BASE and record FEATURE/evidence/regression-testing/cycle1-sinkguard-diff.md. + - Command: `git -C WORKTREE diff --numstat 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`; `git -C WORKTREE diff --name-only 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster TaskMaster.Test`; `git -C WORKTREE status --porcelain --untracked-files=all -- TaskMaster TaskMaster.Test`. + - Acceptance, all required: the numstat row reports 0 deleted lines and between 35 and 50 added lines for the SinkGuard partial (expected 40: 2 for E1 plus 38 for E2; a deleted-line count above 0 means an existing line was rewritten and is `EXISTING LINE CHANGED`: stop); the name-only listing under `TaskMaster` and `TaskMaster.Test` consists of exactly one line, `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (any other line is `FOOTPRINT EXCEEDS WRITE SET`: stop); the porcelain listing under those two paths is either empty (the edit was committed by an orchestrator phase-boundary commit, in which case the diff listing still carries the file) or exactly one entry, ` M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`; the union of the two listings contains the SinkGuard path (positive control). + +### Phase 2 — Final QA Toolchain Loop, Coverage Delta, Scope, Closure and Delivery + +No code file is edited in Phase 2. The loop follows D-8: a SinkGuard rewrite by P2-T1 restarts the loop once from P2-T1 (recorded as `PASS-2:` sections in the same artifacts); any other failure of P2-T1 to P2-T5 stops the plan with its artifact, and the fix returns to the orchestrator as a further remediation round that re-enters at P2-T1. + +- [x] [P2-T1] Apply repository-wide formatting from the worktree root (TaskMaster.sln tree, `.csharpierignore` applies) with `dotnet tool run csharpier format .` and record FEATURE/evidence/qa-gates/cycle1-csharpier-format.md. + - Command: `CMD-HASH-SINKGUARD` and `git -C WORKTREE status --porcelain --untracked-files=all` before the format; `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier format .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH-SINKGUARD` and the same porcelain command again. + - Acceptance, all required: `EXIT_CODE: 0`; the `Formatted N files` line is recorded as a processed count, not an assertion; the before-and-after tree observation holds: the `HASH` value after the format equals the value before it and the two porcelain listings are identical. In pass 1 a differing hash restarts the loop once from this task as stated above, and pass 2 compares against the hash recorded after the pass-1 format; a differing hash in pass 2 is `FORMAT NOT STABLE`: stop. A porcelain entry that changed and lies outside FEATURE, `.claude/agent-memory/` and the SinkGuard path is `FORMAT TOUCHED OUT-OF-SCOPE FILE`: stop. +- [x] [P2-T2] Verify formatting read-only from the worktree root (TaskMaster.sln tree) with `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/cycle1-csharpier-check.md. + - Command: `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [Environment]::CurrentDirectory = (Get-Location).Path; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'` + - Acceptance: `EXIT_CODE: 0`, the `Checked N files` line is recorded (the success-case line is `Checked N files in ms.` and names no path), and no path is reported as unformatted. +- [x] [P2-T3] Run the analyzer gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, `TASKID` p2-t3) and record FEATURE/evidence/qa-gates/cycle1-msbuild-analyzer.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `COORDINATOR_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded beside `ANALYZER-BASELINE-WARNINGS:` as an observation. +- [x] [P2-T4] Run the type-check gate on TaskMaster.sln with `CMD-REBUILD` (`GATEARGS` `/p:TreatWarningsAsErrors=true`, `TASKID` p2-t4) and record FEATURE/evidence/qa-gates/cycle1-msbuild-nullable.md (`Command:` `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`). + - Acceptance, all required: `EXIT_CODE: 0`; `ERRORS: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`; `COORDINATOR_DIAGNOSTIC_LINES: 0`; `WARNINGS:` recorded beside `NULLABLE-BASELINE-WARNINGS:` as an observation. +- [x] [P2-T5] Run the test-and-coverage gate with scripts/vscode/Invoke-MSTestWithCoverage.ps1 through `CMD-COVERAGE-RUNNER` and then, only when the runner exit code is 0, `CMD-COVERAGE-POST`, and record FEATURE/evidence/qa-gates/cycle1-coverage.md. + - Artifact: `Timestamp:`; `Command:` `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`; `EXIT_CODE:` the `RUNNER_EXIT_CODE:`; `Output Summary:` (at most 20 lines) carrying the exit code, `LINE-FLOOR:`, `BRANCH-FLOOR:`, the `First-party coverage:` line, the `ROOT` line, `COORD-LINES`, `COORD-BRANCHES`, `COORD-LINE-RATE:`, `COORD-BRANCH-RATE:`, the three `CLASS-NODE` rows, `FINAL-FAILED-FQN-COUNT:` and `FINAL-TEST-TOTAL:`; then `Details:` with `DISCOVERED_LINE:`, `THRESHOLD_MESSAGE:`, `COLLECT_FAILURE_MESSAGE:`, `DOCUMENT_PRESENT:`, `TRX_PRESENT:`, `SUMMARY_FILE_PRESENT:`, `TEST-DEFINITIONS:`, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the summary verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, every `COORD-FILE` row, `COORD-CLASS-NODES:` and the Grep read-outs named below. `coverage\remediation-964.cobertura.xml` and `coverage\remediation-964.trx` stay on disk, git-ignored; no raw document is committed (CLAUDE.md Committed Test Evidence Format). + - Command (class-node read-out, after the post step): the Grep tool counts over `coverage/remediation-964.cobertura.xml` and over `coverage/final-964.cobertura.xml` (explicit file paths) for the patterns `]*filename="TaskMaster\x5CRibbon\x5CEngineToggleStateCoordinator\.Messages\.cs">` (`NODE-RATE-ONE`) and `]*filename="TaskMaster\x5CRibbon\x5CEngineToggleStateCoordinator\.Messages\.cs">` (`NODE-RATE-HALF`). + - Acceptance, all required: `RUNNER_EXIT_CODE` is 0 with an empty `COLLECT_FAILURE_MESSAGE:` and an empty `THRESHOLD_MESSAGE:` (a non-zero exit is `TEST STEP FAILED`: stop without a re-run, D-5); `DOCUMENT_PRESENT: True` and `TRX_PRESENT: True`; `LINE-FLOOR: MET` and `BRANCH-FLOOR: MET`; `FAILED-FQN-COUNT: 0` with `TEST-DEFINITIONS:` at least 1 and no `FAILED-FQN` row; the summary first line begins `Test run outcome:` and its second line reads `Total 7390, executed 7390, passed 7390, failed 0.` (the first-cycle total 7388 plus the two data rows; a total of 7388 means the new rows were not discovered and is `NEW TEST NOT DISCOVERED`: stop); the projection contains the `TaskMaster` package; every `COORD-FILE` row reads `nodes=1` and `COORD-CLASS-NODES: 3` (otherwise `PARTIAL CLASS ATTRIBUTION UNSUPPORTED`: stop); `COORD-LINES covered=203 valid=203`; `COORD-BRANCHES covered=44 valid=44` (R-1 closes the one uncovered branch of the first-cycle 43/44, and no production branch was added or removed); the Messages `COORD-FILE` row reads `branches-covered=2 branches-valid=2`; the Messages `CLASS-NODE` row reads `branch-rate=1` and the main and Prime `CLASS-NODE` rows read `branch-rate=1` (target 1 for every coordinator class node); the Grep read-out over `coverage/remediation-964.cobertura.xml` gives `NODE-RATE-ONE` 1 and `NODE-RATE-HALF` 0, and over `coverage/final-964.cobertura.xml` gives `NODE-RATE-ONE` 0 and `NODE-RATE-HALF` 1 (the false-before control: the same patterns report the other value on the pre-change document, so the check can fail); no absolute path in the artifact. +- [x] [P2-T6] Compare baseline and post-change coverage for the coordinator files under TaskMaster/Ribbon and record FEATURE/evidence/qa-gates/cycle1-coverage-comparison.md (sources: FEATURE/evidence/remediation-baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/cycle1-coverage.md); then append the final half of the fail-before proof to the dossier of P1-T7. + - Acceptance, all required: the artifact carries `Timestamp:`, `Command:` (the two source artifacts read), `EXIT_CODE: 0` and an `Output Summary:` with `BASELINE-FIRST-PARTY:` and `FINAL-FIRST-PARTY:` (repository line and branch percentages as printed; observations, because the repository-wide rate is not deterministic run to run; the gate on them is the floors of P2-T5), `BASELINE-COORD-LINES: 203/203` and `FINAL-COORD-LINES: 203/203`, `BASELINE-COORD-BRANCHES: 43/44 (97.73)` and `FINAL-COORD-BRANCHES: 44/44 (100)`, `BASELINE-MESSAGES-BRANCH-RATE: 0.5` and `FINAL-MESSAGES-BRANCH-RATE: 1`, and `NEW-CODE-COVERAGE: not applicable, no production line or branch was added` (the changed lines are test lines, excluded from the coverage denominator). Clauses, each recorded `MET` or `NOT MET` with its two values: final coordinator line count equals the baseline (no line removed from coverage); final covered branches equal final valid branches and exceed the baseline covered branches by exactly 1; the final Messages class-node branch-rate is 1; both final floors met. Any `NOT MET` stops. The dossier of P1-T7 gains an appended `CLASS-NODE-PROOF:` section naming the baseline value 0.5 and the final value 1. +- [x] [P2-T7] Record the single clean toolchain pass of TaskMaster.sln (P2-T1 to P2-T5) in FEATURE/evidence/qa-gates/cycle1-toolchain-pass.md. + - Acceptance: the artifact carries `Timestamp:`, `Command:` listing the four CLAUDE.md commands verbatim in order (`dotnet tool run csharpier format .` with `dotnet tool run csharpier check .`; the analyzer `/t:Rebuild`; the `TreatWarningsAsErrors` `/t:Rebuild`; `Invoke-MSTestWithCoverage.ps1`), `EXIT_CODE: 0` and an `Output Summary:` naming each step's artifact and result, the pass number (1, or 2 after the admitted restart) and the test-step outcome (`TEST-STEP: PASS`, runner exit 0 and zero failed tests). +- [x] [P2-T8] Verify the change footprint of the cycle against CYCLE-BASE and the Write Set of FEATURE/remediation-plan.2026-10-03T08-43.md, and record FEATURE/evidence/qa-gates/cycle1-footprint.md. + - Command: `git -C WORKTREE diff --name-only 6b8e935c177128d2f455f7bcd2fedc7deff6e30f`; `git -C WORKTREE status --porcelain --untracked-files=all`; `git -C WORKTREE diff --name-only 6b8e935c177128d2f455f7bcd2fedc7deff6e30f -- TaskMaster TaskMaster.Test`; negative control `git -C WORKTREE diff --name-only 94287369908cc920b21b0e3256314f988ad7d2f5 -- TaskMaster TaskMaster.Test`. + - Acceptance, all required: every path in the first listing and every porcelain entry is `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`, lies under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/`, or lies under `.claude/agent-memory/` (ambient, never staged); the path `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` appears in neither listing (no acceptance criterion was edited); the third listing consists of exactly one line, the SinkGuard path (no production file, csproj or other test file changed this cycle); positive control: the union of the first listing and the porcelain listing contains the SinkGuard path and `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md` (a file recorded by an orchestrator phase-boundary commit appears in the diff listing and need not appear in the porcelain listing). Negative control, required: the control listing is non-empty and contains the line `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs`, a path the first-cycle work changed and this cycle must not, which shows that the path-set rule above reports `FOOTPRINT EXCEEDS WRITE SET` when an out-of-scope code path differs from its anchor (otherwise `FOOTPRINT CONTROL INERT`: stop). Any other path in the first listing or porcelain is `FOOTPRINT EXCEEDS WRITE SET`: stop. Both listings and the control are recorded. +- [x] [P2-T9] Measure every fixture partial under TaskMaster.Test/Ribbon with `CMD-LINECOUNT` and record FEATURE/evidence/qa-gates/cycle1-line-counts.md. + - Acceptance, all required: `TEST-PARTIALS: 7`; the `LINES` value of every partial is at most 500; the SinkGuard value is at least 195 (it grew from 169) and is recorded as `SINKGUARD-LINES-FINAL:` equal to `SINKGUARD-LINES-AFTER:` of P1-T4; the primary fixture `LINES` value is 481 (unchanged; any other value is `PRIMARY FIXTURE CHANGED`: stop). A value above 500 is `FILE SIZE CEILING EXCEEDED`: stop. +- [x] [P2-T10] Record the closure of findings R-1 and R-2 in FEATURE/evidence/other/cycle1-finding-closure.md, without editing issue.md. + - Acceptance: the artifact carries `Timestamp:`, `Command:` (the artifacts read), `EXIT_CODE: 0` and an `Output Summary:` with one line per finding. `R-1: MET` only when P1-T2 shows the `TOKENS-R1` counts, P1-T6 shows `R1-NAME` with `rows=` at least 2 and `passed=` equal to it, P2-T5 shows the Messages `CLASS-NODE` `branch-rate=1` with `COORD-BRANCHES covered=44 valid=44`, and P2-T8 shows the SinkGuard partial as the only changed code file; `R-2: MET` only when P1-T1 shows both `TOKENS-R2` counts at their first-edit values and the placement inside the both-sinks test, P1-T6 shows `R2-NAME` `rows=1 passed=1`, and P1-T8 shows 0 deleted lines; otherwise the finding is recorded `NOT MET` with the failing values and the plan outcome is INCOMPLETE. The artifact also states that AC1 to AC8 of issue.md are unchanged and still checked (P0-T2 and P2-T8). +- [x] [P2-T11] Hand off for the reduced (minor) audit and record FEATURE/evidence/other/cycle1-reduced-audit-handoff.md. + - Acceptance: the artifact carries `Timestamp:`, the finding source (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T08-50.md`, findings CR-1 and CR-4) and the AC source (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`, unchanged), the `R-1` and `R-2` statuses copied from cycle1-finding-closure.md, the evidence paths of every artifact written by P0-T1 to P2-T10 and of this artifact itself, then, under a heading WRITTEN AFTER THIS RECORD, the two fixed paths docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle1-commit-record.md (written by P2-T12) and docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-evidence-hygiene.md (written by P2-T13), with no result claimed for either, the reduced artifact checks for the auditor (the fixture run, the coverage comparison with the class-node read-out, the footprint gate and the hygiene gate), and the statement that CR-2, CR-3 and observations O-1 to O-5 are out of scope for this cycle. +- [x] [P2-T12] Record the pre-commit state in FEATURE/evidence/other/cycle1-commit-record.md before anything is staged. + - Command: `git -C WORKTREE status --porcelain --untracked-files=all`; `git -C WORKTREE rev-parse --abbrev-ref HEAD`; `git -C WORKTREE rev-parse HEAD`. + - Acceptance, all required: the artifact carries `Timestamp:`, `Command:`, `EXIT_CODE: 0` and `Output Summary:`; `BRANCH:` equals `bug/engine-toggle-coordinator-947-review-residuals-964`; `PRE-COMMIT-HEAD:` is the printed 40-hex-digit value; the porcelain listing is recorded and every entry is the SinkGuard path, lies under FEATURE or lies under `.claude/agent-memory/`; the artifact states the staging rule (explicit paths only: the SinkGuard path and the FEATURE folder), the commit subject `test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions` and the statement that the commit hash and the push output are reported in the executor return and not written into the repository (D-7). The hash is a pre-commit observation, so the artifact does not claim any commit-time value. +- [x] [P2-T13] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 after every artifact except the hygiene record itself exists, and record FEATURE/evidence/qa-gates/cycle1-evidence-hygiene.md. + - Acceptance: `FILES_SCANNED=` at least 70 (derivation: the 40 Markdown files present when this plan was authored, namely issue.md, the executed plan, the policy audit, the code review, the feature audit, the remediation inputs and 34 evidence files, plus this plan, 41; plus the 29 evidence files of this plan other than cycle1-evidence-hygiene.md, which this task writes after the sweep: 9 remediation-baseline, 8 regression-testing, 9 qa-gates and 3 other; 41 + 29 = 70), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of a `preflight-clearance.*.md` record is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report. A non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder (the original stays under `coverage/`). +- [ ] [P2-T14] Stage the cycle by explicit path and verify the staged set. + - Command: `git -C WORKTREE add -- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964`; `git -C WORKTREE diff --cached --name-only`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance, all required: every line of the cached listing is `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` or lies under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/` (the same path-set rule whose negative control P2-T8 shows can fail); the cached listing contains the SinkGuard path, `.../evidence/qa-gates/cycle1-evidence-hygiene.md` and `.../evidence/other/cycle1-commit-record.md` (positive controls that the sweep record and the commit record are part of the commit); no `.claude/agent-memory/` path and no path under `TaskMaster/` is staged; the porcelain listing after staging shows no `??` entry under FEATURE and no ` M` entry for the SinkGuard path (everything of the cycle is staged). The hook that gates a commit of a `.cs` file needs the orchestrator checkpoint; a denial is reported verbatim and stops this task. `git add -A` and `git add .` are never used. +- [ ] [P2-T15] Commit the staged cycle with one `git -C WORKTREE commit -m "test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions"` command (a single `-m`; any attribution trailer lines the executing session requires are appended as further `-m` paragraphs; no heredoc, redirection or substitution), then verify the result. + - Command: the commit; `git -C WORKTREE log -1 --format=%s`; `git -C WORKTREE show --name-only --format= HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance, all required: the commit exits 0; the subject printed by `log -1` begins `test(964): cover the null or empty engine key refusal path`; the `show --name-only` listing equals the cached listing of P2-T14 (the same path-set rule holds); the porcelain listing after the commit contains no entry other than `.claude/agent-memory/` paths and, at most, the plan file `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md`, modified only by the check-offs of P2-T14, this task and P2-T16, which are made after the staging snapshot (any other entry is `UNCOMMITTED CYCLE FILE`: stop). The printed commit hash is reported in the executor return. A hook denial is reported verbatim and stops this task. +- [ ] [P2-T16] Push the branch with `git -C WORKTREE push origin bug/engine-toggle-coordinator-947-review-residuals-964` and verify that the remote ref equals the committed head. + - Command: the push; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse refs/remotes/origin/bug/engine-toggle-coordinator-947-review-residuals-964`; `git -C WORKTREE status --porcelain --untracked-files=all`; `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md`. + - Acceptance, all required: the push exits 0 (a rejected or non-fast-forward push is `PUSH REJECTED`: stop and report, with no force option); the two `rev-parse` outputs are equal (the remote-tracking ref was updated by the push to the head that holds the cycle commit, so the equality can fail if the push did nothing); the porcelain listing contains no entry other than `.claude/agent-memory/` paths and, at most, the plan file of this plan, whose numstat row against HEAD then reports an equal number of inserted and deleted lines of at most 3, an empty output counting as 0 and 0 (the check-offs of P2-T14 to P2-T16, left to the orchestrator's next phase-boundary commit). The push output and the two hashes are reported in the executor return and are not written to the repository (D-7). From 01dcbe119b74fecb949452397b18883b9731a242 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 09:37:13 -0400 Subject: [PATCH 14/17] docs(964): record remediation cycle 1 post-commit check-offs P2-T14 to P2-T16 Co-Authored-By: Claude Opus 5.5 --- .../remediation-plan.2026-10-03T08-43.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md index 44503a8f3..8444e26c1 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md @@ -431,12 +431,12 @@ No code file is edited in Phase 2. The loop follows D-8: a SinkGuard rewrite by - Acceptance, all required: the artifact carries `Timestamp:`, `Command:`, `EXIT_CODE: 0` and `Output Summary:`; `BRANCH:` equals `bug/engine-toggle-coordinator-947-review-residuals-964`; `PRE-COMMIT-HEAD:` is the printed 40-hex-digit value; the porcelain listing is recorded and every entry is the SinkGuard path, lies under FEATURE or lies under `.claude/agent-memory/`; the artifact states the staging rule (explicit paths only: the SinkGuard path and the FEATURE folder), the commit subject `test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions` and the statement that the commit hash and the push output are reported in the executor return and not written into the repository (D-7). The hash is a pre-commit observation, so the artifact does not claim any commit-time value. - [x] [P2-T13] Run `CMD-HYGIENE` over every Markdown file under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964 after every artifact except the hygiene record itself exists, and record FEATURE/evidence/qa-gates/cycle1-evidence-hygiene.md. - Acceptance: `FILES_SCANNED=` at least 70 (derivation: the 40 Markdown files present when this plan was authored, namely issue.md, the executed plan, the policy audit, the code review, the feature audit, the remediation inputs and 34 evidence files, plus this plan, 41; plus the 29 evidence files of this plan other than cycle1-evidence-hygiene.md, which this task writes after the sweep: 9 remediation-baseline, 8 regression-testing, 9 qa-gates and 3 other; 41 + 29 = 70), `ACCOUNT_HITS=0`, `MACHINE_HITS=0`, `DRIVE_USERS_HITS=0` and `RAW_DOCUMENTS=0`. A non-zero host count is attributed by the `HIT-FILE` rows, each naming one offending file as its parent directory name, a slash and its file name; it is repaired by replacing each occurrence with REDACTED-PATH in every file a `HIT-FILE` row names and re-running this task, except that a `HIT-FILE` row naming `other/` followed by the file name of a `preflight-clearance.*.md` record is not repaired by this plan: it is `PREPARATION RECORD HOST HIT`: stop and report. A non-zero `RAW_DOCUMENTS` is repaired by deleting that copy from the feature folder (the original stays under `coverage/`). -- [ ] [P2-T14] Stage the cycle by explicit path and verify the staged set. +- [x] [P2-T14] Stage the cycle by explicit path and verify the staged set. - Command: `git -C WORKTREE add -- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964`; `git -C WORKTREE diff --cached --name-only`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance, all required: every line of the cached listing is `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` or lies under `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/` (the same path-set rule whose negative control P2-T8 shows can fail); the cached listing contains the SinkGuard path, `.../evidence/qa-gates/cycle1-evidence-hygiene.md` and `.../evidence/other/cycle1-commit-record.md` (positive controls that the sweep record and the commit record are part of the commit); no `.claude/agent-memory/` path and no path under `TaskMaster/` is staged; the porcelain listing after staging shows no `??` entry under FEATURE and no ` M` entry for the SinkGuard path (everything of the cycle is staged). The hook that gates a commit of a `.cs` file needs the orchestrator checkpoint; a denial is reported verbatim and stops this task. `git add -A` and `git add .` are never used. -- [ ] [P2-T15] Commit the staged cycle with one `git -C WORKTREE commit -m "test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions"` command (a single `-m`; any attribution trailer lines the executing session requires are appended as further `-m` paragraphs; no heredoc, redirection or substitution), then verify the result. +- [x] [P2-T15] Commit the staged cycle with one `git -C WORKTREE commit -m "test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions"` command (a single `-m`; any attribution trailer lines the executing session requires are appended as further `-m` paragraphs; no heredoc, redirection or substitution), then verify the result. - Command: the commit; `git -C WORKTREE log -1 --format=%s`; `git -C WORKTREE show --name-only --format= HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance, all required: the commit exits 0; the subject printed by `log -1` begins `test(964): cover the null or empty engine key refusal path`; the `show --name-only` listing equals the cached listing of P2-T14 (the same path-set rule holds); the porcelain listing after the commit contains no entry other than `.claude/agent-memory/` paths and, at most, the plan file `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md`, modified only by the check-offs of P2-T14, this task and P2-T16, which are made after the staging snapshot (any other entry is `UNCOMMITTED CYCLE FILE`: stop). The printed commit hash is reported in the executor return. A hook denial is reported verbatim and stops this task. -- [ ] [P2-T16] Push the branch with `git -C WORKTREE push origin bug/engine-toggle-coordinator-947-review-residuals-964` and verify that the remote ref equals the committed head. +- [x] [P2-T16] Push the branch with `git -C WORKTREE push origin bug/engine-toggle-coordinator-947-review-residuals-964` and verify that the remote ref equals the committed head. - Command: the push; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse refs/remotes/origin/bug/engine-toggle-coordinator-947-review-residuals-964`; `git -C WORKTREE status --porcelain --untracked-files=all`; `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md`. - Acceptance, all required: the push exits 0 (a rejected or non-fast-forward push is `PUSH REJECTED`: stop and report, with no force option); the two `rev-parse` outputs are equal (the remote-tracking ref was updated by the push to the head that holds the cycle commit, so the equality can fail if the push did nothing); the porcelain listing contains no entry other than `.claude/agent-memory/` paths and, at most, the plan file of this plan, whose numstat row against HEAD then reports an equal number of inserted and deleted lines of at most 3, an empty output counting as 0 and 0 (the check-offs of P2-T14 to P2-T16, left to the orchestrator's next phase-boundary commit). The push output and the two hashes are reported in the executor return and are not written to the repository (D-7). From 2b626cd8b04e0b2ac6eb473beaab76632f4b40f2 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 09:48:12 -0400 Subject: [PATCH 15/17] docs(964): checkpoint in-progress cycle 1 re-audit artifacts (COMMIT NOW) Policy audit and code review written so far; feature audit still in progress. Co-Authored-By: Claude Opus 5.5 --- .../code-review.2026-10-03T09-50.md | 75 +++++ .../policy-audit.2026-10-03T09-50.md | 283 ++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T09-50.md create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T09-50.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T09-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T09-50.md new file mode 100644 index 000000000..f9e10ab7a --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/code-review.2026-10-03T09-50.md @@ -0,0 +1,75 @@ +# Code Review: engine-toggle-coordinator-947-review-residuals (Issue #964), remediation cycle 1 exit + +- Date: 2026-10-03 (review label `2026-10-03T09-50`, caller-supplied; see the policy audit's "Timestamp derivation") +- Branch: `bug/engine-toggle-coordinator-947-review-residuals-964`, head `01dcbe119b74fecb949452397b18883b9731a242`, base origin/main `993fdd01566dee82e5f37acb761a600feaaa1454` (merged into the item at `981abef77`); cycle base `6b8e935c1`, cycle code commit `b27cf3bd2` +- Scope reviewed: the full origin/main..HEAD diff: `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (M), `TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (A), `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (A), `TaskMaster/TaskMaster.csproj` (M), `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (M), `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (A, extended in cycle 1), `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (M), `TaskMaster.Test/TaskMaster.Test.csproj` (M), plus the feature folder (remediation inputs, plan and 30 cycle evidence files) and the promoted record. +- Method (no Bash): the caller's verbatim diff at head read in full; the SinkGuard partial read in full on disk; the coordinator main file re-read at the refusal path, click boundary and `TryInvokeSink`; the primary fixture re-read at the data-row precedent and the Harness; the catch, sink-call-site, banned-API and retired-phrase censuses re-derived by Grep; the two raw Cobertura documents read at the root and at the three coordinator `` nodes; every cycle evidence file read; the worktree reflog read for head, cycle commits and clock. + +## Executive Summary + +**Verdict: PASS.** 0 Blocking findings; 5 Non-blocking findings carried or new (CR-1 closed, CR-2 Informational, CR-3 Informational, CR-4 closed, CR-5 Informational new) plus the policy audit's P-1 (Minor, evidence provenance, non-code); 7 observations (O-1 to O-7). + +Cycle 1 did exactly what the remediation inputs asked and nothing else. The one changed code file gains a data-driven test (`[DataRow(null)]`, `[DataRow("")]`) that drives `HandleToggleClickAsync` down the refusal path with an unusable key and asserts no throw, one notice containing the `(null)` token, no error, no engine member and no invalidation; and the both-sinks-throw test gains the two symmetry assertions its sibling already carried. The diff is 41 added and 0 deleted lines, so no existing assertion moved or weakened. The coverage effect is the one the finding predicted: the Messages partial's Cobertura class node moves from `branch-rate="0.5"` to `branch-rate="1"` at the same line of the two documents, the TaskMaster package moves exactly one branch from missed to covered with no line change, and the type reads 44/44 branches. Because no production line changed, every first-cycle verdict on the split, the guard, the ordering invariants and the documentation stands as written in the review labelled 08-50; this review re-read the load-bearing regions on disk and found them identical to the diff. + +Two small items are new. CR-5: the SinkGuard partial's type-level summary still describes the file as "regression tests for a throwing notification sink plus a guard for the #948 record placement" and does not mention the null-or-empty-key test, a one-clause omission in a touched file. P-1 (in the policy audit): fourteen cycle artifacts first carried composed `Timestamp:` labels and were re-stamped to one host-clock reading with an in-field note; disclosed, no figure affected, non-blocking. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Closed (was Non-blocking Minor, CR-1 / R-1) | `TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` | line 19 (`RenderEngineName`), reached from `BuildUnavailableMessage` | The true arm of `string.IsNullOrEmpty(engineName) ? NullEngineNameToken : engineName` is now executed by the two rows of `HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing` (`SinkGuard.cs` 127-153). | None; closed. | The Messages class node reads `line-rate="1" branch-rate="1"` in the post-cycle document and `branch-rate="0.5"` in the pre-cycle control at the same line 230924; COORD-BRANCHES 43/44 -> 44/44; the test asserts `Notifications[0]` contains `"(null)"`, which only the true arm produces. | `coverage/remediation-964.cobertura.xml` 230924; `coverage/final-964.cobertura.xml` 230924; `evidence/qa-gates/cycle1-coverage.md`; `evidence/qa-gates/cycle1-coverage-comparison.md`; `evidence/regression-testing/cycle1-fixture-run.md` (rows=2 passed=2) | +| Non-blocking (Informational, CR-2, unchanged) | `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` | lines 287-300 (`TryInvokeSink`) | `catch (Exception ex)` contains every exception a sink throws. | No change (accepted exception X-1 in the policy audit). | CLAUDE.md C#4 admits a broad catch at a clear boundary with documentation; both present; `RibbonCommandBoundary.SafeLog` precedent. | Read of lines 264-300 (unchanged since the first cycle). | +| Non-blocking (Informational, CR-3, unchanged) | `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` | lines 188-199 | Two closures per refused click. | No change. | General Code Change Policy 6.1; user-paced path. | Read of lines 184-212 (unchanged). | +| Closed (was Non-blocking Informational, CR-4 / R-2) | `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` | lines 112-113 | `harness.Engines.VerifyNoOtherCalls();` and `harness.Invalidations.Should().BeEmpty(...)` now close `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow`, matching the sibling at 77-78. | None; closed. | Scenario symmetry restored; the test still passes (rows=1 passed=1). | Read of lines 88-114; `evidence/regression-testing/cycle1-r2-edit.md` (R2-PLACEMENT 112 and 113); `cycle1-sinkguard-diff.md` (0 deleted lines). | +| Non-blocking (Informational, CR-5, new, related: touched file) | `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` | lines 9-19 (type-level ``) | The summary enumerates the partial's contents as the throwing-notification-sink regression tests "plus a guard for the issue #948 record placement" and does not mention the null-or-empty-key refusal-path test the cycle added, so a reader of the summary alone underestimates the file. The test's own `` (120-126) is complete. | Add one clause to the type-level summary, for example after "reported once through `logError`": "; a refused click with a null or empty key is rendered with the `(null)` token". One line; no behaviour change. | CLAUDE.md 5.2 / C#6.2 (comments synchronized with the code); the coordinator's related-defect rule treats comment drift in a touched file as a finding for this item. Informational because the summary is incomplete rather than wrong and the method-level summary is accurate. | Read of lines 9-19 against the three regions at 22, 118 and 157. | + +The policy audit's P-1 (fourteen re-stamped `Timestamp:` labels; Minor; non-code; non-blocking) is not repeated in this table because it concerns evidence provenance rather than code; see policy-audit.2026-10-03T09-50.md section 8. + +## Detailed Review + +### The cycle change (`EngineToggleStateCoordinatorTests.SinkGuard.cs`, 169 -> 210 lines) + +**R-1 test (118-155).** `[DataTestMethod]` with `[DataRow(null)]` and `[DataRow("")]` on `string engineName`, the exact attribute shape the primary fixture already uses at 101-107 for `GetPressed_WithNullOrWhitespaceKey_ReturnsFalseWithoutPrimeOrInvalidate`. Arrange: `new Harness { EnginesAvailable = false }` with recording, non-throwing sinks. Act: `Func act = () => harness.Coordinator.HandleToggleClickAsync(engineName)`. Assert: `NotThrowAsync`; `Notifications.Should().ContainSingle()`; `Notifications[0].Should().Contain("(null)")`; `Errors.Should().BeEmpty()`; `Engines.VerifyNoOtherCalls()` on the strict mock; `Invalidations.Should().BeEmpty()`. Each assertion carries a because-reason. The path is synchronous to its `return` (main file 186-202), so the awaited call observes the whole outcome; nothing sleeps, polls or reads a clock. The test discriminates: with a usable key the notice renders the key (the pre-existing `..._WithNullEngines_NotifiesOnceAndInvokesNothing` passes `"Spam"`), so `Contain("(null)")` can only be satisfied by the true arm of `RenderEngineName`. The literal `"(null)"` duplicates the private constant `NullEngineNameToken`; that is unavoidable from a test in another assembly without exposing the constant, and the constant's documented purpose is exactly this rendering, so the coupling is acceptable. + +**R-2 assertions (112-113).** Appended after the `BeSameAs` chain and before the closing brace of the both-sinks-throw test; byte-identical to the sibling's lines 77-78. They pin that a refused click whose two sinks both throw still touches no engine member and invalidates nothing, which the production code guarantees by never reaching `ExecuteToggleAsync` on that path. + +**Diff shape.** 41 added, 0 deleted lines (NUMSTAT in `cycle1-sinkguard-diff.md`; the caller's diff shows the same). The repository-wide `csharpier format .` left the file's SHA-256 unchanged (`cycle1-csharpier-format.md`), so the layout is the formatter's. The only layout the file-scoped format rewrote was the `Notifications[0]` chain (`cycle1-format.md`), which now reads as the three-line chain at 146-149. + +**Size.** 210 lines (reviewer Grep count; `cycle1-line-counts.md` SINKGUARD-LINES-FINAL 210). The primary fixture is unchanged at 481 (O-1). + +### Nothing regressed (whole-branch re-check) + +- Production: `cycle1-footprint.md` listing 3 names exactly one path and the negative control (diff against the first-cycle anchor) lists all eight branch code paths, so the gate can fail; the reviewer's re-read of main 120-220 and 260-302 matches the caller's diff line for line. The two `catch` clauses (208, 295), the four guarded sink calls (190, 196, 210; Prime.cs 185) and the single definition (287) are as the first review recorded. +- Ordering invariants (#942 / #944 / #947 / #948): unchanged code; Prime.cs 63 before 64 (register before start), 183-191 before 194 (report before clear), record only inside the `true` branch (190), `ContainsKey` guard (181). All eleven invariant-named tests pass in both cycle runs (`coordinator-tests-baseline.md`, `cycle1-fixture-run.md`), and the `GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain` guard still passes. +- Documentation (AC5, AC7): unchanged code; the retired-phrase Grep over the three production files returns 0; the summaries and remarks at main 160-183 and 264-286 and Prime.cs 10-26, 71-82, 131-164 read as the first review quoted them. +- Project files: `TaskMaster.csproj` 466-468 and `TaskMaster.Test.csproj` 352-364 as before; the SinkGuard partial was already registered, so no csproj edit was needed or made. +- Toolchain: one clean pass inside the cycle (`cycle1-toolchain-pass.md`): format and check (1640 files), analyzer rebuild 0/0, TreatWarningsAsErrors rebuild 0/0, `Invoke-MSTestWithCoverage.ps1` 7390/7390 with both floors met. + +### Tests (policy) + +- **Framework and libraries (CUT1/CUT2):** MSTest `[DataTestMethod]`/`[DataRow]`; the strict Moq mock from `Harness`; FluentAssertions with because-reasons. `using` set unchanged (System, Threading.Tasks, FluentAssertions, MSTest, Moq); nothing unused. +- **Determinism:** reviewer Grep over the seven partials for Thread.Sleep, Task.Delay, DateTime.Now/UtcNow, Stopwatch, SpinWait, `.Wait()`, `.Result`, Path.GetTemp, File., Directory., DoNotParallelize: 0 hits. +- **Fail-before:** structurally impossible for both changes (coverage over correct behaviour; assertions over correct behaviour). The dossier `fail-before-exception.2026-10-03T09-23.md` carries `WhyFailingRunImpossible:` and an alternative proof whose every value this review found on disk: R1-NAME 0 before (`sinkguard-partial-baseline.md`) and present at line 130 after; fixture 43 -> 45; the Messages class node 0.5 -> 1 with the false-before control read on the pre-cycle document (the same Grep pattern returns the other value there). +- **Scenario completeness:** the null and empty inputs the token exists for are now both exercised; whitespace-only keys are outside `IsNullOrEmpty` and are rendered literally (O-7). + +### Evidence and process + +- 30 cycle artifacts under the canonical sub-kinds; every one carries `Timestamp:`, `Command:`, `EXIT_CODE:` and an output summary; none under `artifacts/`. +- Hygiene: CMD-HYGIENE 70 files, 0/0/0 hits, 0 raw documents; reviewer Grep for drive-letter paths and the account name finds only URLs. +- Committed Test Evidence Format: projection plus one-line summary for the coverage run; trx-derived summaries for the test runs; raw documents retained under the gitignored `coverage/` directory (`.gitignore` 146, 147, 150). +- Timestamps: see the policy audit's P-1. In short, fourteen artifacts carry an honest in-field note that their first label was composed; the corrected labels are monotone and bracketed by the cycle-open commit (08:44:10 -0400) and the cycle commit (09:32:42); the host-clock labels that follow agree with the remediation Cobertura root epoch (09:26:44) and the commit epoch to the minute. Not blocking; the pattern should not recur. +- Plan adherence: all 33 tasks checked (Grep for `^- [ ]`: 0); the plan's `Status:` and `Last Updated:` header lines were not updated after execution (O-6). + +## Observations (not findings) + +- O-1: the primary fixture `EngineToggleStateCoordinatorTests.cs` remains at 481/500 lines (unchanged in the cycle, as the remediation inputs required). The next harness member or test added there needs a `.Harness.cs` partial. +- O-2: none of the three coordinator files carries `#nullable enable` (pre-existing; unchanged). +- O-3: the canonical `artifacts/csharp/coverage.xml` path is absent in both checkouts; the committed projections and the local gitignored raw documents were used, per the standing ruling (recurring). +- O-4: the PR context artifact pair is absent in the review worktree; the session checkout's pair belongs to the #968 branch (head 78e24a68c). Scope was verified from the caller's diff, the footprint evidence of both cycles and the files on disk. +- O-5: `quality-tiers.yml` is absent at the repository root (pre-existing; promoted by the #956 review). +- O-6: `remediation-plan.2026-10-03T08-43.md` still reads `Status: Authored, awaiting preflight` and `Last Updated: 2026-10-03T08-43` with all 33 tasks checked, and no cycle-1 `preflight-clearance.*.md` exists under `evidence/other/` (only the first-cycle record of 2026-10-02T07-50). Plan-header drift; the orchestrator may update the two header lines at its next phase-boundary commit. Whether the cycle's preflight was recorded in gitignored orchestrator state is not observable from the tree. +- O-7: `RenderEngineName` substitutes the token for null or empty keys only (`IsNullOrEmpty`), whereas `GetPressed` and `EngineToggleCatalog` treat whitespace-only keys as unusable (the primary fixture's `[DataRow(" ")]` row). A whitespace-only key on the refusal path therefore renders literally inside the quotes of the notice. This is the documented contract of `NullEngineNameToken` ("when the caller supplied null or empty"), pre-dates this item (#505), and is not reachable from the ribbon callbacks, which pass catalog keys; recorded for completeness, not as a defect. + +## Unrelated defects + +None found in the files read for this review. O-3 and O-5 are pre-existing repository-level conditions, not defects of this item, and both are already tracked. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T09-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T09-50.md new file mode 100644 index 000000000..ba0b2602b --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/policy-audit.2026-10-03T09-50.md @@ -0,0 +1,283 @@ +# Policy Audit: engine-toggle-coordinator-947-review-residuals (Issue #964), remediation cycle 1 exit + +- Timestamp: 2026-10-03T09-50 (caller-supplied label, chosen by the coordinator to sort after every artifact already in the folder; it is not a host-clock reading by this reviewer, see "Timestamp derivation" below) +- Branch: bug/engine-toggle-coordinator-947-review-residuals-964 +- Head: 01dcbe119b74fecb949452397b18883b9731a242 (worktree reflog, last entry: `docs(964): record remediation cycle 1 post-commit check-offs P2-T14 to P2-T16`, epoch 1791034633) +- Base: origin/main 993fdd01566dee82e5f37acb761a600feaaa1454, merged into the item at 981abef77657adcc90d7c116a6b4c6500b79ea29 (reflog entry `merge origin/main`, epoch 1790993935). origin/main is an ancestor of the head, so the item change set is the two-dot diff origin/main..HEAD, which the caller supplied verbatim (scratchpad file `964-code-r1.diff`, read in full) and which agrees with the files on disk and with evidence/qa-gates/footprint-scope.md plus evidence/qa-gates/cycle1-footprint.md. +- Review kind: re-audit at the exit of remediation cycle 1 over the WHOLE branch diff (the same scope as the review labelled 2026-10-03T08-50). Cycle 1 changed one code file, TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs (+41 / -0, commit b27cf3bd24b6cf8c5ce9275488bb143e36a61d33, epoch 1791034362), plus the remediation plan and 30 evidence files; no production file, project file, other test file or acceptance criterion changed in the cycle (cycle1-footprint.md listing 3: exactly one path; negative control non-empty). +- Work mode: minor-audit (issue.md line 12); acceptance-criteria source: the `## Acceptance Criteria` section of issue.md only (AC1 to AC8, lines 27 to 34) +- Reviewer: feature-review, no-Bash mode (caller directive). Every check was performed with Read, Grep and Glob against the item worktree, the committed evidence under evidence/, the caller-supplied verbatim diff, the gitignored raw Cobertura documents at their local paths (coverage/final-964.cobertura.xml as the pre-cycle control, coverage/remediation-964.cobertura.xml as the post-cycle document) and the worktree reflog as head reference and clock. +- Timestamp derivation: the reflog gives the only clock readable in this session. Converting its epochs at the recorded -0400 offset: cycle opened at 6b8e935c1 (epoch 1791031450, 08:44:10); cycle commit b27cf3bd2 (1791034362, 09:32:42); head 01dcbe119 (1791034633, 09:37:13). The remediation Cobertura root reads `timestamp="1791034004"` (09:26:44), three minutes before the 09-30 label of evidence/qa-gates/cycle1-coverage.md, and the commit-record and hygiene labels (09-32) match the cycle commit minute. The caller-supplied label 09-50 is later than every label in the folder and every reflog epoch; whether it is at or before the host clock at the moment of this write could not be checked without a shell and is recorded as such. The review labelled 08-50 was likewise caller-supplied; see finding P-1 for the executor-side label issue. + +## Executive Summary + +Overall verdict: PASS. 0 Blocking findings. 0 findings of class autonomous, external_dependency, policy_hold, awaiting_ci or human_decision_required that block the pull request. Remediation inputs: not produced. Both cycle-1 findings are closed on code and evidence: R-1 (CR-1, the untested null-or-empty arm of `RenderEngineName`) is closed by the data-driven test at SinkGuard.cs 127-153, with the Messages partial's Cobertura class node now reading `branch-rate="1"` (remediation document line 230924) against `0.5` in the pre-cycle control, and the coordinator type at 44/44 branches; R-2 (CR-4, the two missing symmetry assertions) is closed at SinkGuard.cs 112-113. Nothing regressed: AC1 to AC8 re-evaluated PASS, the toolchain passed in one clean pass inside the cycle, the suite reads 7390/7390 (7388 + the two data rows), the coordinator fixture 45/45, and the four ordering invariants are unchanged because no production line changed. Two new non-blocking findings are recorded: P-1 (Minor, evidence provenance: fourteen cycle artifacts first carried composed `Timestamp:` labels and were re-stamped to a single host-clock reading with an in-field note) and CR-5 (Informational, the SinkGuard partial's type-level summary does not mention its new region). CR-2 and CR-3 remain Informational with a "No change" recommendation. + +| Area | Verdict | Evidence summary | +|---|---|---| +| General Unit Test Policy | PASS | One new data-driven MSTest test (two rows) and two appended FluentAssertions statements; no Thread.Sleep, Task.Delay, wall-clock read, temporary file or parallelism attribute (reviewer Grep over the seven fixture partials: 0 hits); fixture 43/43 at the cycle base, 45/45 after; suite 7388 -> 7390, 0 failed | +| General Code Change Policy | PASS | Test-only remediation of two review findings, 41 added and 0 deleted lines in one file; every file at or under 500 lines (production 302/197/86, test partials 481/277/210/290/215/175/77); toolchain single pass inside the cycle; cycle footprint exactly one code path | +| C# Code Change Policy | PASS | csharpier check exit 0 (1640 files); analyzer /t:Rebuild 0 errors 0 warnings, CSC_OUT 2/2; TreatWarningsAsErrors /t:Rebuild 0 errors 0 warnings, CSC_OUT 2/2, no /p:Nullable=enable; no production change in the cycle, so the first-cycle production verdicts stand | +| C# Unit Test Policy | PASS | MSTest, Moq, FluentAssertions throughout; first-party lines 85.97%, branches 80.13% (floors 80/75 per CLAUDE.md, 85/75 per .claude/rules, both met); coordinator type 203/203 lines, 44/44 branches (was 43/44) | +| Coverage (C#) | PASS | Repo-wide 85.97% lines / 80.13% branches after the cycle (85.96% / 80.10% before); every coordinator class node line-rate 1 and branch-rate 1; new production code of the item (TryInvokeSink, BuildNotifyFailedMessage, rewritten refusal path) 100% lines and branches; no production code added in the cycle | +| Evidence hygiene | PASS | Executor CMD-HYGIENE at P2-T13: 70 files, 0 account, 0 machine, 0 drive-path hits, 0 raw documents; reviewer Grep over the feature folder for drive-letter paths and the account name: only URLs (issue URL, XML namespace URIs, SDK download URL) match; Glob: Markdown only | +| Committed Test Evidence Format | PASS | Cycle coverage run committed as the JaCoCo package projection plus the one-line first-party summary; cycle test runs committed as trx-derived summaries; raw documents stay under the gitignored coverage/ directory (.gitignore lines 146, 147, 150) | +| Evidence timestamps | PASS with finding P-1 (non-blocking) | Fourteen cycle artifacts carry `Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read)`; the remaining cycle labels (09-17, 09-24, 09-25, 09-30, 09-31, 09-32) are monotone, bracketed by the cycle-open and cycle-commit epochs, and the 09-30 coverage label sits three minutes after the Cobertura root epoch | + +## Rejected Scope Narrowing + +No scope narrowing was detected in the caller prompt. The following caller statements were evaluated and accepted as factual, as tooling constraints, as the work-mode rule or as verification focus rather than as narrowing: + +- "Do NOT use the Bash tool at all." A tooling constraint, not a scope constraint. The audit scope remains the full branch diff against origin/main; the caller's verbatim full diff was read, every changed code file was read or re-read on disk, and the cycle's single changed file was read in full. +- "re-audit (remediation cycle 1 exit) of the WHOLE feature branch for issue 964, with the same inputs and scope as the original reduced review (no scope narrowing)." Consistent with the scope invariant; the "reduced" qualifier is the minor-audit work-mode rule from issue.md line 12, which governs the AC source, not the policy scope. +- "Verify: R-1 (CR-1) and R-2 (CR-4) are closed in code and evidence ...; every finding of the prior review is either closed or still correctly non-blocking; nothing regressed (AC1..AC8 ...)". Verification focus added on top of the full audit, not a restriction of it; every policy area below was evaluated over the full diff. +- "The cycle changed only TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs (+41/-0) in code." A factual statement, confirmed against the caller's diff, cycle1-footprint.md and cycle1-sinkguard-diff.md (NUMSTAT 41 0); it was not used to limit which files were audited. +- "Write nothing outside FEATURE (your own agent-memory note excepted)." An output constraint consistent with the Required Outputs. + +## Evidence Location Compliance + +- Branch diff scan for files under artifacts/baselines/, artifacts/qa/, artifacts/evidence/ or artifacts/coverage/: none. The origin/main..HEAD change set consists of the eight code paths, one promoted record under docs/features/potential/promoted/ and paths under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/ only (caller diff; footprint-scope.md; cycle1-footprint.md). +- All executor evidence lives under docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/{baseline,regression-testing,qa-gates,other,remediation-baseline}/. Cycle 1 added 9 remediation-baseline, 8 regression-testing (including the fail-before exception dossier), 10 qa-gates and 3 other files; reviewer Glob of the feature folder: 71 Markdown files, no other file type. +- validate_evidence_locations.py --root .: not run (Bash was forbidden for this review; Glob for the script over the worktree returned nothing, so it is not present in this checkout). The manual scan above substitutes; no violation observed. +- EVIDENCE_LOCATION_OVERRIDE_REJECTED: none required; the caller supplied no non-canonical evidence path. The remediation plan records the same (`EVIDENCE_LOCATION_OVERRIDE_REJECTED: none supplied`). +- PR context artifacts (artifacts/pr_context.summary.txt, artifacts/pr_context.appendix.txt): absent in the review worktree (Glob of the exact path returned nothing). The session checkout carries a pair generated 2026-10-03 07:59:49 UTC for head 78e24a68c of the #968 branch, which is a different item and is not evidence for this review. Regeneration was not possible without a shell or the collection tool. Scope was derived from the caller-supplied verbatim diff, the committed footprint evidence of both cycles and the files on disk, three agreeing sources. +- Raw coverage documents: coverage/final-964.cobertura.xml (pre-cycle control; root line-rate 0.859565, branch-rate 0.801019, lines 56629/65881, branches 13683/17082, epoch 1791029515) and coverage/remediation-964.cobertura.xml (post-cycle; root line-rate 0.859717, branch-rate 0.801253, lines 56639/65881, branches 13687/17082, epoch 1791034004) exist locally in the worktree, gitignored, not committed. The canonical path artifacts/csharp/coverage.xml is absent in both checkouts (observation O-3, recurring); the committed JaCoCo package projection plus one-line summary are the forms CLAUDE.md "Committed Test Evidence Format" requires, and the standing ruling treats executor-committed feature-folder coverage evidence as the present artifact. + +## 1. General Unit Test Policy Compliance + +### 1.1 Core principles + +| Principle | Verdict | Evidence | +|---|---|---| +| Independence | PASS | The new test constructs its own Harness per row (SinkGuard.cs 135); no static state is read or written; no parallelism attribute anywhere in the seven partials (reviewer Grep for DoNotParallelize: 0); the two appended assertions read only the test's own harness | +| Isolation | PASS | The new test targets one behaviour, the refusal path with an unusable key, and asserts the five facts the finding named (no throw, one notice containing the token, no error, no engine member, no invalidation); the appended assertions pin two facts the sibling test already pinned for the same arrange | +| Fast execution | PASS | The refusal path is synchronous up to its `return`; no bounded wait, timer or pump; the fixture run of 45 tests completed inside the P1-T6 vstest invocation with no Sequence file | +| Determinism | PASS | No clock, random value or filesystem is read; reviewer Grep over the seven partials for Thread.Sleep, Task.Delay, DateTime.Now, DateTime.UtcNow, Stopwatch, SpinWait, .Wait(), .Result, Path.GetTemp, Environment.TickCount, File., Directory.: 0 hits; the data rows null and "" are constants | +| Readability | PASS | Descriptive name stating scenario and outcome (`HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing`); XML `` on the test stating what it exercises; Arrange / Act / Assert markers (134, 137, 140); a because-reason on every assertion (142, 145, 149, 150, 152) | + +### 1.2 Coverage + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|---|---|---|---|---|---|---| +| C# | 6 | 7390 | 7390 passed, 0 failed | 85.96% lines / 80.10% branches | 85.97% lines / 80.13% branches | 100% lines / 100% branches | +| TypeScript | 0 | N/A | N/A | N/A | N/A | N/A | +| PowerShell | 0 | N/A | N/A | N/A | N/A | N/A | +| Python | 0 | N/A | N/A | N/A | N/A | N/A | + +Files Changed counts .cs files only over the whole branch (six: three production, of which two added, and three test, of which one added); the two project files TaskMaster/TaskMaster.csproj and TaskMaster.Test/TaskMaster.Test.csproj are the remaining code paths. Baseline Coverage is the item's original baseline on the pre-change tree (evidence/baseline/coverage-baseline.md, P0-T14 of the first cycle: 56609/65855 lines, 13680/17078 branches). Post-Change Coverage is the cycle-1 final run (evidence/qa-gates/cycle1-coverage.md, P2-T5: 56639/65881 lines, 13687/17082 branches). New Code Coverage is measured over the executable lines and branches this item added to production: TryInvokeSink (10 of 10 lines, EngineToggleStateCoordinator.cs 288-300), BuildNotifyFailedMessage (8 of 8 lines, Messages.cs 51-58), the rewritten refusal path and click-boundary catch body (186-201 and 210), and the four branches added to the type; the cycle added no production line or branch (cycle1-coverage-comparison.md: `NEW-CODE-COVERAGE: not applicable, no production line or branch was added`), so the item-level figure stands and the one arm the item had left uncovered is now covered too. + +Coverage source statement: the figures above are read from the committed projections and summaries (evidence/baseline/coverage-baseline.md; evidence/qa-gates/coverage-final.md as the pre-cycle state; evidence/remediation-baseline/coverage-baseline.md; evidence/qa-gates/cycle1-coverage.md; evidence/qa-gates/cycle1-coverage-comparison.md), each carrying the first-party summary line, the root counters, the package-level JaCoCo projection and the coordinator class-node rows, and were cross-checked by this review against the root elements and the three coordinator `` nodes of the local raw documents coverage/final-964.cobertura.xml (main line-rate 1 branch-rate 1; Prime.cs 1 / 1; Messages.cs 1 / 0.5 at line 230924) and coverage/remediation-964.cobertura.xml (main 1 / 1 at line 230619; Messages.cs 1 / 1 at line 230924; Prime.cs 1 / 1 at line 231046). Every run used the same route, `Invoke-MSTestWithCoverage.ps1`, over nine test assemblies, so they are comparable. + +Verdict lines: + +- C# coverage verdict: PASS (repo-wide first-party lines 85.97% and branches 80.13% from the committed post-cycle projection and the raw root element; above the CLAUDE.md floors of 80% lines and 75% branches and above the 85% / 75% floors in .claude/rules; up from 85.96% / 80.10% at the item baseline and at the pre-cycle state). +- C# new-code coverage: PASS. 100% of the executable lines and 100% of the branches this item added to production are covered; after the cycle the type reads 203/203 lines and 44/44 branches with every coordinator class node at line-rate 1 and branch-rate 1. +- C# changed-production-file coverage: PASS on both limbs. EngineToggleStateCoordinator.cs 89/89 lines and 22/22 branches; Prime.cs 72/72 lines and 20/20 branches; Messages.cs 42/42 lines and 2/2 branches (was 1/2; cycle1-coverage.md COORD-FILE rows). The pre-existing uncovered arm recorded as CR-1 in the review labelled 08-50 is covered by the new test; nothing regressed (BASELINE-COORD-LINES 203/203 = FINAL-COORD-LINES 203/203; branches 43/44 -> 44/44). +- C# package-level corroboration: the TaskMaster package counters moved from 802 missed / 2503 covered lines and 211 missed / 523 covered branches (pre-cycle) to 802 / 2503 lines and 210 / 524 branches (post-cycle): exactly one branch moved from missed to covered with no line change, which is the arithmetic signature of R-1 alone. The repo-wide line gain of +10 covered lines and the remaining +3 covered branches sit in packages no file of this branch touches (run-to-run variance of the kind recorded on earlier reviews); the floors are the gate and both are met. +- PowerShell coverage gate: PASS by vacuity (zero PowerShell files changed on this branch, so the changed-line no-regression requirement has no line to evaluate; no PoshQC format, analyze or test gate was owed or run; artifacts/pester/powershell-coverage.xml was not consulted). +- TypeScript and Python: zero files changed on this branch; no verdict is owed. + +### Coverage Evidence Checklist + +- C# baseline coverage artifact: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/baseline/coverage-baseline.md` (item baseline; committed one-line first-party summary, root counters, JaCoCo package projection and coordinator class-node rows) with `evidence/remediation-baseline/coverage-baseline.md` (cycle baseline, read from the committed first-cycle final evidence and the local raw document coverage/final-964.cobertura.xml, gitignored) +- C# post-change coverage artifact: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/qa-gates/cycle1-coverage.md` with `evidence/qa-gates/cycle1-coverage-comparison.md` (same forms; raw document coverage/remediation-964.cobertura.xml present locally, gitignored; canonical artifacts/csharp/coverage.xml absent in the worktree) +- TypeScript baseline coverage artifact: none consulted (zero TypeScript files changed on this branch) +- TypeScript post-change coverage artifact: none consulted (zero TypeScript files changed on this branch) +- PowerShell baseline coverage artifact: none consulted (zero PowerShell files changed on this branch) +- PowerShell post-change coverage artifact: none consulted (zero PowerShell files changed on this branch) +- Python baseline coverage artifact: none consulted (zero Python files changed on this branch) +- Python post-change coverage artifact: none consulted (zero Python files changed on this branch) +- Per-language comparison summary: the per-language comparison block of this document + +### 1.2.1 Per-Language Coverage Comparison + +- C#: Baseline: 85.96% lines (56609/65855) / 80.10% branches (13680/17078). Post-change: 85.97% lines (56639/65881) / 80.13% branches (13687/17082). Change: +0.01% lines / +0.03% branches at two decimals (+30 covered of +26 valid lines, +7 covered of +4 valid branches over the whole branch; within the cycle alone, +10 covered lines of 0 added and +4 covered branches of 0 added, of which the TaskMaster package accounts for exactly +1 branch, the R-1 arm). New/changed-code coverage: 100%. Disposition: PASS. Evidence: evidence/baseline/coverage-baseline.md, evidence/qa-gates/coverage-final.md, evidence/remediation-baseline/coverage-baseline.md, evidence/qa-gates/cycle1-coverage.md, evidence/qa-gates/cycle1-coverage-comparison.md, root elements and coordinator class nodes of coverage/final-964.cobertura.xml and coverage/remediation-964.cobertura.xml. +- TypeScript: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero TypeScript files changed on this branch. +- PowerShell: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero PowerShell files changed on this branch. +- Python: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero Python files changed on this branch. + +### 1.2.2 Coverage Artifact State + +| Language | Artifact consulted | State | Disposition | +|---|---|---|---| +| C# | Committed projections, summaries and class-node rows under evidence/baseline, evidence/remediation-baseline and evidence/qa-gates; raw Cobertura root elements and class nodes of the pre-cycle and post-cycle documents read locally | Present; canonical artifacts/csharp/coverage.xml absent in the worktree (observation O-3, recurring) | PASS | +| TypeScript | none | zero files changed | no verdict owed | +| PowerShell | none | zero files changed | no verdict owed | +| Python | none | zero files changed | no verdict owed | + +Coverage exclusion policy check (.claude/rules/general-unit-test.md): the branch adds no coverage-config exclude entry and no ExcludeFromCodeCoverage attribute (the cycle touched no production file or configuration; the first-cycle Grep result stands: the only occurrence in the three production files is the pre-existing class remark stating the type is deliberately NOT excluded). Not Blocking. + +### 1.3 Scenario completeness + +| Scenario | Verdict | Evidence | +|---|---|---| +| Positive flows | PASS | Refused click with a healthy notification sink and a usable key still notifies once and invokes nothing (pre-existing `HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing`, Passed in both cycle runs); healthy prime and toggle paths unchanged and Passed | +| Negative flows | PASS | Throwing notification sink (AC1); reported once through the log sink with the same exception instance, no engine member invoked, no invalidation (AC2); both sinks throwing (AC2, now with the two symmetry assertions, R-2); null and empty engine key on the refusal path rendered as the `(null)` token, nothing logged, nothing invoked, nothing invalidated (R-1, two data rows) | +| Edge cases | PASS | The record-placement guard test exercises the sink-throws-on-first-report, returns-on-second sequence; the empty-string row of the new test is the boundary the token exists for; the pre-existing #948 and #947 partials are byte-identical to base and Passed | +| Error handling | PASS | Every sink call site contained by TryInvokeSink (production unchanged in the cycle); the click boundary still reports the toggle fault unchanged; ExecuteToggleAsync still propagates | +| Concurrency | PASS | No production change in the cycle; the lock-scoped registration and the keyed TryRemove as the last statement of CompletePrime stand (Prime.cs 49-65 and 194); the Race partial is unchanged except the first-cycle remark; all invariant-named tests Passed in both cycle runs | +| State transitions | PASS | Marker registered -> prime -> report attempted -> recorded only on a normal sink return -> marker cleared; observed by the guard test and the unchanged PrimeFaultOrdering and PrimeRegistration partials | + +### 1.4 Arrange-Act-Assert + +PASS. The new test carries Arrange / Act / Assert markers (SinkGuard.cs 134, 137, 140); every assertion carries a because-reason string; the `Func act` under Act with the awaited `NotThrowAsync` under Assert mirrors the existing refusal-path tests of the same partial and the fixture test at EngineToggleStateCoordinatorTests.cs 342-346. The two appended assertions (112-113) sit after the last existing assertion of their test and before its closing brace (cycle1-r2-edit.md R2-PLACEMENT, confirmed by Read). + +### 1.5 External dependencies and temporary files + +PASS. No Outlook COM, file system, network or process is touched by any changed test; the engines are a strict Moq mock and the three sinks are recording delegates. Reviewer Grep over the seven fixture partials for Path.GetTemp, File., Directory., Thread.Sleep and Task.Delay: 0 hits. The unchanged RepeatFaultSuppression partial imports System.IO for an IOException instance used as a second fault kind, which is not I/O. + +### 1.6 Test file location + +PASS (repository convention). The cycle edited the existing partial TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs beside the six other partials of the same fixture, mirroring the per-project *.Test layout used for every C# project in this repository; no test file was created or colocated with production source; no project-file change was needed (the partial was already registered at TaskMaster.Test.csproj line 364). + +### 1.7 Determinism infrastructure + +PASS. No time is consumed by any changed test; no randomness is used; banned APIs in test code: Thread.Sleep 0, Task.Delay 0, Date/clock reads 0 over the seven fixture partials (reviewer Grep). The coordinator type reads no clock. + +## 2. General Code Change Policy Compliance + +| Item | Verdict | Evidence | +|---|---|---| +| Before making changes (plan, AC source) | PASS | remediation-inputs.2026-10-03T08-43.md names R-1 and R-2 with constraints; remediation-plan.2026-10-03T08-43.md exists with all 33 tasks checked (reviewer Grep for `^- [ ]`: 0); Phase 0 policy reads recorded (remediation-baseline/phase0-instructions-read.md); the cycle base 6b8e935c1 confirmed as merge-base and the code tree unchanged at the start (remediation-baseline/scope-and-anchor.md) | +| Bugfix workflow step 1 (failing regression test first) | PASS | For the item: evidence/regression-testing/refusal-path-fail-before.md (three refusal-path tests Failed with the fix provably absent; first cycle). For the cycle: a failing run is structurally impossible (both changes add coverage or assertions over behaviour already correct), and the fail-before exception dossier evidence/regression-testing/fail-before-exception.2026-10-03T09-23.md carries `WhyFailingRunImpossible:` and the alternative proof (R1-NAME 0 before / 1 after; fixture 43 -> 45; Messages class-node branch-rate 0.5 before / 1 after, with the false-before control read on the pre-cycle document). SearchScope: FEATURE/evidence/regression-testing/; SearchPatterns: fail-before-exception.*.md; SearchResult: the dossier named above | +| Bugfix workflow step 2 (minimal targeted fix) | PASS | Cycle: 41 added, 0 deleted lines in one test file (cycle1-sinkguard-diff.md NUMSTAT; reviewer Read of the file against the caller's diff); no production, csproj or other test file changed (cycle1-footprint.md listing 3). Item: one private static helper, three call-site rewrites, one message builder, docs, pure-move split (first-cycle evidence, unchanged) | +| Bugfix workflow step 3 (verify locally, toolchain in order) | PASS | evidence/qa-gates/cycle1-toolchain-pass.md: pass 1 clean for format (SinkGuard hash identical before and after the repository-wide format), check, analyzer rebuild, TreatWarningsAsErrors rebuild and the Invoke-MSTestWithCoverage.ps1 route (7390/7390) | +| Design principles (simplicity, reusability, extensibility, separation) | PASS | The new test reuses the fixture's Harness and the existing `[DataTestMethod]` / `[DataRow]` precedent (EngineToggleStateCoordinatorTests.cs 101-107) rather than adding a helper; the production design verdicts of the first cycle stand unchanged | +| Classes, functions, APIs | PASS | No production surface changed in the cycle; the item's internal surface is byte-equal to base per the first-cycle SPAN-HASH rows | +| Error handling | PASS | Two `catch` clauses remain in the type (EngineToggleStateCoordinator.cs 208 and 295; reviewer Grep: every other hit is a `catch` documentation mention); no empty catch; the sink guard is the documented boundary catch accepted as X-1 | +| Logging | PASS | No new logging channel; the new test asserts `Errors` empty on the refusal path with healthy sinks | +| File size limit (500 lines) | PASS | Reviewer Grep line counts: EngineToggleStateCoordinator.cs 302, Prime.cs 197, Messages.cs 86; test partials 481 (primary), 77, 175, 277, 290, 210 (SinkGuard, from 169), 215; all agree with evidence/qa-gates/cycle1-line-counts.md | +| Naming | PASS | PascalCase members; camelCase locals (`harness`, `act`, `engineName`); the test name states scenario and outcome | +| Public APIs and compatibility | PASS | No public API; no project-file change in the cycle | +| Dependencies | PASS | None added | +| I/O boundaries | PASS | No I/O introduced; the type remains host-neutral | + +## 3. Language-Specific Code Change Policy Compliance + +Language in scope: C# only. + +| Item | Verdict | Evidence | +|---|---|---| +| Formatting (csharpier via dotnet tool run) | PASS | evidence/regression-testing/cycle1-format.md (file-scoped format then check, `Checked 1 files`, exit 0); evidence/qa-gates/cycle1-csharpier-format.md (repository-wide, `Formatted 1640 files`, SinkGuard SHA-256 identical before and after, porcelain identical); evidence/qa-gates/cycle1-csharpier-check.md (`Checked 1640 files in 7110ms.`, exit 0, no path listed) | +| Linting (analyzer rebuild, /t:Rebuild, EnableNETAnalyzers, EnforceCodeStyleInBuild) | PASS | evidence/qa-gates/cycle1-msbuild-analyzer.md: exit 0, ERRORS 0, WARNINGS 0 (baseline 0), CSC_OUT 2/2, WRITESET_DIAGNOSTIC_LINES 0, COORDINATOR_DIAGNOSTIC_LINES 0 | +| Type checking (TreatWarningsAsErrors rebuild, no /p:Nullable=enable) | PASS | evidence/qa-gates/cycle1-msbuild-nullable.md: exit 0, 0 errors, 0 warnings, CSC_OUT 2/2; command text matches CLAUDE.md character for character | +| Nullable annotations | PASS | No file in the cycle carries or changes a `#nullable` directive; the three production files remain nullable-disabled (first-cycle observation O-2 stands) | +| Partial-class split | PASS | Unchanged in the cycle: three files declaring `internal sealed partial class EngineToggleStateCoordinator`, registered at TaskMaster.csproj 466-468 (reviewer Grep) | +| XML docs on non-obvious contract | PASS | The new test carries a ``; production documentation unchanged and accurate (AC5, AC7 re-read). The partial's type-level summary omits its new region: CR-5, Informational | +| Internal surface | PASS | No new member outside the test partial | +| Analyzer suppressions | PASS | None added (reviewer Read of the 41 added lines: no #pragma, SuppressMessage or ExcludeFromCodeCoverage) | + +## 4. Language-Specific Unit Test Policy Compliance + +| Item | Verdict | Evidence | +|---|---|---| +| MSTest framework | PASS | `[DataTestMethod]` with two `[DataRow]` attributes (SinkGuard.cs 127-129) inside the existing `[TestClass]` partial fixture; the attribute spelling matches the primary fixture's precedent at lines 101-104; MSTest namespace imported once | +| Moq for mocks | PASS | The strict Mock from the shared Harness; `Engines.VerifyNoOtherCalls()` in the new test (151) and appended to the both-sinks test (112) | +| FluentAssertions | PASS | All assertions use Should() (NotThrowAsync, ContainSingle, Contain, BeEmpty); no MSTest Assert introduced | +| Repo-wide coverage floors | PASS | 85.97% lines (floor 80% per CLAUDE.md, 85% per rules), 80.13% branches (floor 75%) | +| New module/class/method >= 90% | PASS | Item-level: TryInvokeSink 100% (10/10 lines, 2/2 branches), BuildNotifyFailedMessage 100% (8/8 lines); no new production member in the cycle | +| No regression on changed lines | PASS | No production line changed in the cycle; coordinator 203/203 lines at both cycle stages; branches 43/44 -> 44/44 | +| Prohibited behaviors (sleeps, retries, timing hacks, weakened assertions) | PASS | 0 banned-API hits; 0 deleted lines in the cycle, so no assertion was weakened or removed; the four other partials and the primary fixture are unchanged since the first cycle (cycle1-footprint.md) | +| Test toolchain route | PASS | Step 4 ran the CLAUDE.md route `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1` (RUNNER_EXIT_CODE 0; DOCUMENT_PRESENT, TRX_PRESENT, SUMMARY_FILE_PRESENT all True; THRESHOLD_MESSAGE and COLLECT_FAILURE_MESSAGE empty) | + +## 5. Test Coverage Detail + +| File | Change type | Coverage observation | Disposition | +|---|---|---|---| +| TaskMaster/Ribbon/EngineToggleStateCoordinator.cs | Modified production in the first cycle (496 -> 302 lines); unchanged in cycle 1 | Class node line-rate 1 (89/89), branch-rate 1 (22/22) in the post-cycle document (line 230619) | PASS | +| TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs | Added production in the first cycle; unchanged in cycle 1 | Class node line-rate 1 (72/72), branch-rate 1 (20/20) (line 231046) | PASS | +| TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs | Added production in the first cycle; unchanged in cycle 1 | Class node line-rate 1 (42/42), branch-rate 1 (2/2) (line 230924); was branch-rate 0.5 (1/2) in the pre-cycle document at the same line; the arm covered is `RenderEngineName`'s null-or-empty branch (line 19), reached through `BuildUnavailableMessage` from the new test's two rows | PASS (CR-1 closed) | +| TaskMaster/TaskMaster.csproj | Modified project file in the first cycle (+2 Compile items, 467-468); unchanged in cycle 1 | Not a source file | Discovery proven: three class nodes in both documents | +| TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs | Added test in the first cycle (169); modified in cycle 1 (+41 / -0, now 210) | Outside the denominator by policy | Not measured; 6 results (5 methods, one with two rows) Passed; fail-before dossier for the cycle | +| TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs | Modified test fixture in the first cycle (+12 / -1); unchanged in cycle 1 (481 lines) | Outside the denominator by policy | Not measured; Passed | +| TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs | Modified test in the first cycle (remark only); unchanged in cycle 1 | Outside the denominator by policy | Not measured; Passed | +| TaskMaster.Test/TaskMaster.Test.csproj | Modified project file in the first cycle (+1 Compile item, 364); unchanged in cycle 1 | Not a source file | Discovery proven: the new rows appear in the P1-T6 and P2-T5 runs (fixture 45, suite 7390) | + +Package-level projection (TaskMaster package): LINE missed 802 / covered 2503; BRANCH missed 210 / covered 524 after the cycle (cycle1-coverage.md), from 802 / 2503 and 211 / 523 before it (coverage-final.md), and from 802 / 2477 and 211 / 519 at the item baseline. + +## 6. Test Execution Metrics + +| Run | Scope | Total | Passed | Failed | Source | +|---|---|---|---|---|---| +| Item baseline (first cycle P0-T14, Invoke-MSTestWithCoverage.ps1) | nine test assemblies | 7384 | 7384 | 0 | evidence/baseline/coverage-baseline.md | +| First-cycle final (P2-T5, Invoke-MSTestWithCoverage.ps1) | nine test assemblies | 7388 | 7388 | 0 | evidence/qa-gates/coverage-final.md | +| Cycle-1 fixture baseline (P0-T9, vstest) | EngineToggleStateCoordinatorTests | 43 | 43 | 0 | evidence/remediation-baseline/coordinator-tests-baseline.md (R1-NAME rows=0, false-before) | +| Cycle-1 fixture after the edits (P1-T6, vstest) | EngineToggleStateCoordinatorTests | 45 | 45 | 0 | evidence/regression-testing/cycle1-fixture-run.md (R1-NAME rows=2 passed=2; R2-NAME rows=1 passed=1) | +| Cycle-1 final (P2-T5, Invoke-MSTestWithCoverage.ps1) | nine test assemblies | 7390 | 7390 | 0 | evidence/qa-gates/cycle1-coverage.md | + +Figures compared: cycle final total equals the first-cycle final plus 2 (the two data rows of the one new method); error, timeout, aborted and notExecuted each 0 at every stage (summaries derived from the trx); no Sequence file in any run; FINAL-FAILED-FQN-COUNT 0; TEST-DEFINITIONS 7380 (a data-driven method is one definition with two results). + +## 7. Code Quality Checks + +| Check | Command or method | Result | Verdict | +|---|---|---|---| +| Confidentiality masking scan | Executor CMD-HYGIENE at P2-T13 (ACCOUNT_HITS, MACHINE_HITS, DRIVE_USERS_HITS over 70 files); reviewer Grep over the feature folder for `[A-Za-z]:[\\/]`, the account name and `Users[\\/]` | 0 / 0 / 0; reviewer hits are the issue URL, two XML namespace URIs in the plans and the SDK download URL, none a host path | PASS | +| Raw document scan | Glob over the feature folder for every file; .gitignore lines 146, 147, 150 | 71 Markdown files, no trx, Cobertura or log; raw documents gitignored under coverage/ (RAW_DOCUMENTS=0) | PASS | +| Suppression scan (added lines) | Read of the 41 added lines | No #pragma, [SuppressMessage], [ExcludeFromCodeCoverage] or analyzer suppression | PASS | +| Workflow change scan | Caller diff; cycle1-footprint.md | No .github/, scripts/ or runsettings path changed on the branch | PASS | +| Prohibited-construct scan | Reviewer Grep over the seven fixture partials | Thread.Sleep 0, Task.Delay 0, DateTime.Now/UtcNow 0, Stopwatch 0, SpinWait 0, .Wait() 0, .Result 0, Path.GetTemp 0, File. 0, Directory. 0, DoNotParallelize 0 | PASS | +| Catch-clause census | Reviewer Grep for `catch` over the three production files | Two code occurrences: EngineToggleStateCoordinator.cs 208 (click boundary) and 295 (sink guard); every other hit is a documentation mention; Prime.cs and Messages.cs contain no catch | PASS | +| Sink call-site census | Reviewer Grep over the three production files | `_logError(` three times, each inside a TryInvokeSink lambda (main 196, 210; Prime.cs 185); `_notifyUnavailable(` once, inside a TryInvokeSink lambda (190); `TryInvokeSink(` 5 = one definition (287) + four call sites | PASS | +| Comment-drift census (AC7) | Reviewer Grep for the retired phrases over the three production files; Read of each surviving comment | Retired phrases 0 hits; the HandleToggleClickAsync summary and remarks, the StartObservedPrime remarks, the CompletePrime summary and remarks and the constructor parameter docs match the two catch clauses that exist | PASS | +| Cycle footprint | cycle1-footprint.md (three listings and a negative control); cycle1-sinkguard-diff.md | Exactly one changed code path; 41 added, 0 deleted lines; issue.md in neither listing; negative control non-empty | PASS | +| Tonality scan | Read of the remediation inputs, plan, closure, handoff and every cycle evidence file cited | Neutral, factual wording; no humor, hyperbole or metaphor | PASS | + +## 8. Gaps and Exceptions + +- X-1 (accepted exception, carried from #947 and the review labelled 08-50): TryInvokeSink catches Exception broadly without re-raising. Under CLAUDE.md C#4 this is a boundary catch with documented discard and forwarding, following the RibbonCommandBoundary.ReportFailure / SafeLog precedent. Unchanged in the cycle. Not Blocking. +- P-1 (non-blocking Minor, evidence provenance; no code change): fourteen cycle-1 artifacts (remediation-baseline: scope-and-anchor, sinkguard-partial-baseline, coverage-baseline, bootstrap-probe, csharpier-check-baseline, msbuild-analyzer-baseline, msbuild-nullable-baseline, coordinator-tests-baseline; regression-testing: cycle1-r2-edit, cycle1-r1-edit, cycle1-format, cycle1-token-gates, cycle1-build, cycle1-fixture-run) carry `Timestamp: 2026-10-03T09-23 (host clock read at correction; the label first written was composed, not read)` (reviewer Grep for `composed`: 14 files). The caller reported fifteen; the fail-before dossier (file name and field 09-23) and cycle1-sinkguard-diff.md (field 09-23) carry the same label without the note, and which of them was the fifteenth could not be determined from the tree. Rule engaged: the remediation plan's own evidence rule ("the write time is the `Timestamp:` field") and the evidence-first wording rule of .claude/rules/tonality.md; the skill evidence-and-timestamp-conventions fixes the format, which every label satisfies. Classification: a disclosed deviation, not a falsification. The fourteen labels record the correction time, not each artifact's write time, so the per-artifact write times between the 09-17 policy-read label and the 09-24 format label are not recoverable; no figure in any artifact depends on its label; the labels are monotone with the task order and are bracketed by the cycle-open commit (08:44:10) and the cycle commit (09:32:42); the host-clock labels that follow (09-24, 09-25, 09-30, 09-31, 09-32) agree with the remediation Cobertura root epoch (09:26:44) and the commit epoch to the minute. Severity Minor because the executor disclosed the correction in the field itself and no evidence value is affected. Disposition: no remediation task owed; the pattern (composing a label instead of reading the clock) should not recur, and the orchestrator may wish to carry it to the executor's memory. Note also that the review labels 08-50 and 09-50 were caller-supplied rather than clock-read, which both review artifacts state in their headers; they are later than every evidence label and commit epoch, which is the property the label ordering depends on. +- CR-5 (non-blocking Informational, related, touched file): the type-level `` of EngineToggleStateCoordinatorTests.SinkGuard.cs (lines 9-19) enumerates the throwing-sink regression tests and the #948 record-placement guard but not the null-or-empty-key refusal-path test the cycle added; see code-review.2026-10-03T09-50.md. +- PR context artifact pair absent in the review worktree: scope verified from three agreeing sources instead (section Evidence Location Compliance). +- Canonical C# coverage artifact path absent: committed projections and the local raw documents used, per the standing ruling (observation O-3, recurring across #947, #948, #950, #968 and the review labelled 08-50). +- Preflight record for the remediation cycle: the remediation plan header still reads `Status: Authored, awaiting preflight` and `Last Updated: 2026-10-03T08-43` although all 33 tasks are checked, and no `preflight-clearance.*.md` for the cycle exists under evidence/other/ (SearchScope: FEATURE/evidence/other/ and the FEATURE root; SearchPatterns: preflight-clearance.*.md; SearchResult: evidence/other/preflight-clearance.2026-10-02T07-50.md, the first-cycle record only). Whether the cycle's preflight was recorded in gitignored orchestrator state is not observable from the tree. Observation O-6; not attributable to the executor and not blocking. +- quality-tiers.yml absent at the repository root (Glob: none; pre-existing; tier gates unevaluable; already promoted by the #956 review). Not attributable to this item. + +## 9. Summary of Changes + +Whole branch (unchanged from the review labelled 08-50 except where noted): + +- TaskMaster/Ribbon/EngineToggleStateCoordinator.cs: class made partial; message builders and prime lifecycle moved out; TryInvokeSink added; the refusal path routes the notification through the guard and forwards a notification failure to the log sink through a second guarded call; the click-boundary catch body routes its log call through the guard; documentation rewritten to match. +- TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs (new): GetPrimeTask with the corrected registration-marker documentation; StartPrimeIfNeeded, StartObservedPrime and ApplyPrimeAsync moved verbatim; CompletePrime records a reported fault kind only in the branch taken when TryInvokeSink returns true. +- TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs (new): NullEngineNameToken, RenderEngineName and the five builders, with BuildNotifyFailedMessage added. +- TaskMaster/TaskMaster.csproj: two Compile items. +- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs (new; extended in cycle 1): three refusal-path regression tests (fail-before recorded), one record-placement guard test, and, from cycle 1, one data-driven refusal-path test for a null or empty engine key (two rows) plus two symmetry assertions appended to the both-sinks-throw test. +- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs: OnNotify hook on the Harness. +- TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs: one remark corrected for the #948 suppression rule. +- TaskMaster.Test/TaskMaster.Test.csproj: one Compile item. +- Evidence: 34 first-cycle Markdown files plus 30 cycle-1 files under the feature folder's evidence tree, the remediation inputs and plan, issue.md check-offs and the checked plans; no raw document committed. + +## 10. Compliance Verdict + +PASS. Every policy area evaluates PASS over the full branch diff at head 01dcbe119. Zero blocking findings; nothing requires a further remediation cycle before the pull request is opened. R-1 (CR-1) and R-2 (CR-4) are closed on code and evidence. One non-blocking Minor finding (P-1, evidence provenance, disclosed by the executor) and one non-blocking Informational finding (CR-5) are new; CR-2 and CR-3 stand as Informational with no change recommended. The pull request body should carry `Closes #964`. + +## Appendix A: Test Inventory + +| Test class | Test | Status after the cycle | AC or finding | +|---|---|---|---| +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow | Failed before the fix (first cycle), Passed after; Passed in both cycle-1 runs | AC1 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing | Failed before the fix, Passed after; Passed in both cycle-1 runs | AC2 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow (two assertions appended in cycle 1) | Failed before the fix, Passed after; Passed in both cycle-1 runs | AC2, R-2 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | HandleToggleClickAsync_WithNullOrEmptyKeyAndNullEngines_NotifiesOnceWithNullTokenAndInvokesNothing (null row, "" row; added in cycle 1) | rows=0 at the cycle base (false-before), rows=2 passed=2 after | R-1 | +| EngineToggleStateCoordinatorTests (SinkGuard partial) | GetPressed_WhenLogSinkThrowsOnFaultedPrime_SameFaultKindIsReportedAgain | Passed before and after (guard) | AC3, AC4 | +| EngineToggleStateCoordinatorTests (ThrowingSink partial, unchanged) | GetPressed_WhenLogSinkThrowsOnFaultedPrime_LaterReadStartsNewPrime, GetPressed_WhenLogSinkThrowsOnCanceledPrime_LaterReadStartsNewPrime, GetPressed_WhenLogSinkThrows_FirstPrimeCompletesAndMarkerIsCleared, HandleToggleClickAsync_WhenLogSinkThrowsOnToggleFault_DoesNotThrowAndAttemptsReport | Passed, unchanged | AC3, AC4 | +| EngineToggleStateCoordinatorTests (PrimeFaultOrdering, PrimeRegistration, RepeatFaultSuppression partials, unchanged) | seven invariant tests | Passed, unchanged | AC4 | +| EngineToggleStateCoordinatorTests (Race partial, remark only) | GetPressed_WhenPrimeIsCanceled_LogsErrorAndClearsPrimeMarker and siblings | Passed, unchanged behaviour | AC4 | +| EngineToggleStateCoordinatorTests (primary partial) | HandleToggleClickAsync_WithNullEngines_NotifiesOnceAndInvokesNothing and the other pre-existing tests | Passed | AC2, AC4 | + +Test result count: 7384 at the item baseline, 7388 after the first cycle, 7390 after cycle 1 (one method with two rows added, none removed); fixture 39 -> 43 -> 45. + +## Appendix B: Toolchain Commands Reference + +| Step | Command (as recorded in evidence/qa-gates/cycle1-toolchain-pass.md) | Exit | Iteration | +|---|---|---|---| +| 1 | dotnet tool run csharpier format . | 0 | 1 | +| 1b | dotnet tool run csharpier check . | 0 | 1 | +| 2 | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true | 0 | 1 | +| 3 | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true | 0 | 1 | +| 4 | pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1 (the CLAUDE.md route; 7390/7390; LINE-FLOOR MET, BRANCH-FLOOR MET) | 0 | 1 | + +PowerShell gates (PoshQC MCP format / analyze / test): not run; zero PowerShell files changed on this branch. + +Reviewer commands: none (Bash forbidden). All verification by Read, Grep and Glob against the item worktree, the caller's verbatim diff, the gitignored raw Cobertura documents and the worktree reflog. From d5ff301366f3ac06e405dc7a1c0fc54edf68dbb9 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 09:50:29 -0400 Subject: [PATCH 16/17] docs(964): add cycle 1 re-audit feature audit (PASS, 0 blocking) Co-Authored-By: Claude Opus 5.5 --- .../feature-audit.2026-10-03T09-50.md | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T09-50.md diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T09-50.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T09-50.md new file mode 100644 index 000000000..3116d7d51 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/feature-audit.2026-10-03T09-50.md @@ -0,0 +1,96 @@ +# Feature Audit: engine-toggle-coordinator-947-review-residuals (Issue #964), remediation cycle 1 exit + +- Date: 2026-10-03 (review label `2026-10-03T09-50`, caller-supplied; see the policy audit's "Timestamp derivation") +- Work mode: `minor-audit` (`issue.md` line 12, the only `- Work Mode:` line). AC source: the `## Acceptance Criteria` section of `issue.md` only (heading at line 23; eight checkbox lines 27-34; next heading `## Environment` at line 36). `spec.md` and `user-story.md` are intentionally absent (Glob of the feature folder: none; `remediation-baseline/scope-and-anchor.md` REQUIREMENTS-DOCUMENTS: none). +- Branch: `bug/engine-toggle-coordinator-947-review-residuals-964` +- Review kind: re-audit at the exit of remediation cycle 1 over the whole branch; the cycle changed one test file and no acceptance criterion (`issue.md` appears in neither cycle footprint listing). + +## Scope and Baseline + +| Item | Value | Source | +|---|---|---| +| Base | origin/main `993fdd01566dee82e5f37acb761a600feaaa1454`, merged into the item at `981abef77657adcc90d7c116a6b4c6500b79ea29` (reflog entry `merge origin/main`, epoch 1790993935) | caller prompt; worktree reflog; `evidence/qa-gates/footprint-scope.md` | +| Pre-change text anchor | `94287369908cc920b21b0e3256314f988ad7d2f5` (first-cycle Phase 0 anchor); no TaskMaster or TaskMaster.Test path changed between that anchor and the merge commit | `evidence/baseline/scope-and-anchor.md`; `evidence/qa-gates/footprint-scope.md` negative control 2 | +| Head commit | `01dcbe119b74fecb949452397b18883b9731a242` (`docs(964): record remediation cycle 1 post-commit check-offs P2-T14 to P2-T16`, epoch 1791034633) | worktree reflog, last entry | +| Cycle 1 commits | `96d2975d4` (first-cycle review artifacts), `6b8e935c1` (cycle opened: remediation inputs and plan; cycle base), `b27cf3bd2` (`test(964): cover the null or empty engine key refusal path and symmetric sink-guard assertions`; the one code commit of the cycle), `01dcbe119` (check-offs) | reflog; `evidence/other/cycle1-commit-record.md` (PRE-COMMIT-HEAD 6b8e935c1) | +| Code footprint (origin/main..HEAD) | `M TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` (496 -> 302 lines), `A TaskMaster/Ribbon/EngineToggleStateCoordinator.Prime.cs` (197), `A TaskMaster/Ribbon/EngineToggleStateCoordinator.Messages.cs` (86), `M TaskMaster/TaskMaster.csproj` (+2), `M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.cs` (470 -> 481), `A TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` (210; 169 before the cycle), `M TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.Race.cs` (remark only), `M TaskMaster.Test/TaskMaster.Test.csproj` (+1) | caller verbatim diff; `evidence/qa-gates/footprint-scope.md`; `evidence/qa-gates/cycle1-footprint.md`; files re-read; reviewer Grep line counts | +| Cycle 1 code footprint (6b8e935c1..HEAD) | exactly `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs`, +41 / -0 | `evidence/qa-gates/cycle1-footprint.md` listing 3; `evidence/regression-testing/cycle1-sinkguard-diff.md` NUMSTAT | +| Non-code footprint | feature folder only (issue.md check-offs, two plans, remediation inputs, 64 evidence files, three first-cycle review artifacts) and `docs/features/potential/promoted/2026-10-01-engine-toggle-coordinator-947-review-residuals.md` | same | +| Baseline behaviour (defect) | `HandleToggleClickAsync` called `_notifyUnavailable(...)` unguarded on the refusal path (pre-fix line 178); `GetPrimeTask` documentation described "the in-flight ... prime"; the file was 496/500 lines | `evidence/regression-testing/refusal-path-fail-before.md`; `evidence/regression-testing/split-census.md` BASE-LINES 496 | +| Baseline tests | fixture 39/39; suite 7384/7384 | `evidence/baseline/coordinator-tests-baseline.md`; `evidence/baseline/coverage-baseline.md` | +| Baseline coverage | first-party 85.96% lines / 80.10% branches; coordinator 177/177 lines, 39/40 branches (one class node) | `evidence/baseline/coverage-baseline.md` | +| Pre-cycle state (first-cycle final) | fixture 43/43; suite 7388/7388; first-party 85.96% / 80.10%; coordinator 203/203 lines, 43/44 branches; Messages class node branch-rate 0.5 | `evidence/qa-gates/coverage-final.md`; `evidence/remediation-baseline/coverage-baseline.md`; `coverage/final-964.cobertura.xml` line 230924 re-read | +| Post-cycle state | fixture 45/45; suite 7390/7390; first-party 85.97% / 80.13%; coordinator 203/203 lines, 44/44 branches; every coordinator class node line-rate 1 and branch-rate 1 | `evidence/regression-testing/cycle1-fixture-run.md`; `evidence/qa-gates/cycle1-coverage.md`; `evidence/qa-gates/cycle1-coverage-comparison.md`; `coverage/remediation-964.cobertura.xml` lines 230619, 230924, 231046 re-read | +| PR context artifacts | absent from the review worktree; the session checkout's pair belongs to the #968 branch; not regenerable without a shell. Scope derived from the three agreeing sources above. | Glob; Read | + +## Acceptance Criteria Inventory + +Source: `docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md`, section `## Acceptance Criteria`. All eight are checkbox items and all eight read `- [x]` at head (first-cycle check-offs at P2-T10 to P2-T17; the cycle edited none: `remediation-baseline/scope-and-anchor.md` ISSUE-GREP-COUNTS checked-AC1-8=8, unchecked-AC=0; `cycle1-footprint.md`: issue.md in neither listing). + +| ID | Criterion (verbatim opening) | Checkbox at head | +|---|---|---| +| AC1 | (refusal-path notification guard, regression-first): with the engines accessor returning null and a `notifyUnavailable` sink that throws, `EngineToggleStateCoordinator.HandleToggleClickAsync` completes without throwing. A new MSTest regression test proves this; it is recorded failing against the unmodified coordinator ... and passing after the fix. | `[x]` | +| AC2 | (notification failure is reported, not lost): in the AC1 scenario the notification sink is attempted exactly once, the sink's exception is delivered exactly once to `logError` (the same exception instance), no engine member is invoked, and no control is invalidated. When `logError` also throws in that scenario, `HandleToggleClickAsync` still completes without throwing. Both behaviours are asserted by named MSTest tests. | `[x]` | +| AC3 | (one shared sink guard): all three sink call sites ... route through a single private guard helper; no other `catch` clause that discards a sink exception remains in the coordinator's source files. Verified by reading the split source and by the existing #947 tests in `EngineToggleStateCoordinatorTests.ThrowingSink.cs` passing unchanged. | `[x]` | +| AC4 | (prime-marker ordering invariants preserved across the split): ... the marker is registered before the prime starts; a prime fault is reported before the marker is cleared; a sink that throws leaves the fault kind unrecorded (report still owed); a repeated fault kind for the same engine is reported once. Verified by every existing test in the `EngineToggleStateCoordinatorTests` partials ... passing with no assertion weakened or removed. | `[x]` | +| AC5 | (`GetPrimeTask` documentation accuracy): the `GetPrimeTask` `` and `` describe the registration marker (completed only after the prime outcome has been observed and reported), not "the prime task" or "the in-flight prime". | `[x]` | +| AC6 | (file-size split): `TaskMaster/Ribbon/EngineToggleStateCoordinator.cs` is split into cohesive partial-class files of the same `internal sealed partial class EngineToggleStateCoordinator`, each file at or below 450 lines, each new file registered as a `Compile` item in `TaskMaster/TaskMaster.csproj`; every touched test file stays at or below 500 lines. | `[x]` | +| AC7 | (comment drift in touched files): every comment that counts or locates the coordinator's `catch` clauses ... and the `HandleToggleClickAsync` "never throws" remark match the post-change code, and the constructor's `notifyUnavailable` and `enginesAccessor` parameter docs state the guarded behaviour and the accessor's non-throwing precondition. | `[x]` | +| AC8 | (toolchain and coverage): the CLAUDE.md C# toolchain passes in one clean pass (csharpier check, analyzer `/t:Rebuild`, `TreatWarningsAsErrors` `/t:Rebuild` without `/p:Nullable=enable`, `Invoke-MSTestWithCoverage.ps1`), with no new failing test relative to baseline and the coordinator's line coverage not lower than its baseline figure. | `[x]` | + +## Acceptance Criteria Evaluation + +Each criterion was re-evaluated against the head tree and the cycle evidence. Where the criterion concerns production code, the cycle changed none, and this review re-read the cited regions on disk rather than relying on the first-cycle verdict alone. + +| ID | Status | Evidence and reasoning | +|---|---|---| +| AC1 | PASS | Code (re-read): `EngineToggleStateCoordinator.cs` 186-202, the notification call is the argument of `TryInvokeSink` (189-193), whose `catch (Exception ex)` (295-299) assigns the exception and returns `false`; nothing on the path rethrows. Test: `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_DoesNotThrow` (`SinkGuard.cs` 30-43). Fail-before: `evidence/regression-testing/refusal-path-fail-before.md` (first cycle; EXIT_CODE 1 with ExpectedExitCode 1; fix provably absent; the message names `notify sink failed`). Pass-after: `refusal-path-pass-after.md`; cycle-1 runs `coordinator-tests-baseline.md` and `cycle1-fixture-run.md` both `rows=1 passed=1`; cycle final FAILED-FQN-COUNT 0. | +| AC2 | PASS | `HandleToggleClickAsync_WhenNotifySinkThrowsWithNullEngines_LogsSinkExceptionOnceAndInvokesNothing` (51-79): single notification, single error, `BeSameAs(notifyFailure)`, message contains the key, `Engines.VerifyNoOtherCalls()`, `Invalidations` empty. `HandleToggleClickAsync_WhenNotifyAndLogSinksThrowWithNullEngines_DoesNotThrow` (87-114): both hooks throw; `NotThrowAsync`, single notification, single log attempt, same instance, and, since the cycle, `Engines.VerifyNoOtherCalls()` and `Invalidations` empty (112-113). Code: the forwarding call at 195-198 is itself guarded and its result discarded. Both tests `rows=1 passed=1` in both cycle runs. | +| AC3 | PASS | Call sites (reviewer Grep, head tree): `_notifyUnavailable(` once (main 190, inside a `TryInvokeSink` lambda); `_logError(` three times (main 196 and 210, Prime.cs 185), each inside a `TryInvokeSink` lambda; `TryInvokeSink` is `private static` (main 287). Catch clauses in the three source files: main 208 (click boundary) and main 295 (the guard); Prime.cs and Messages.cs contain none (every other hit is a `catch` doc mention). `ThrowingSink.cs` unchanged on the branch since the first-cycle anchor and untouched in the cycle (`cycle1-footprint.md`); its four tests `rows=1 passed=1` in both cycle runs. | +| AC4 | PASS | Code unchanged in the cycle; re-read: registration before start (Prime.cs 63 then 64, inside the `_primeGate` lock); report before clear (183-188 then 194); the record at 190 is the only statement of the `if (TryInvokeSink(...))` branch; `ContainsKey` guard at 181. Tests: all eleven invariant-named tests and the record-placement guard `rows=1 passed=1` in `coordinator-tests-baseline.md` (cycle base) and `cycle1-fixture-run.md` (after); the cycle deleted 0 lines in its one changed file and touched no other partial, so no assertion was weakened or removed; first-cycle `test-partials-unchanged.md` still describes the other partials. | +| AC5 | PASS | Prime.cs 10-26 unchanged in the cycle and re-read: the summary opens "The registration marker for an engine key" and states the marker "is not the prime task itself"; the returns opens "The registered marker, or Task.CompletedTask when no marker is registered". Reviewer Grep for `The prime task, or`, `The in-flight` and `most recently completed` over the three files: 0. | +| AC6 | PASS | Three files, each `internal sealed partial class EngineToggleStateCoordinator` in `namespace TaskMaster`; reviewer Grep line counts 302 / 197 / 86 (each at or below 450); `TaskMaster.csproj` 466-468 registers all three (reviewer Grep); touched test files 481 (primary), 277 (Race), 210 (SinkGuard), each at or below 500 (`cycle1-line-counts.md` agrees); `TaskMaster.Test.csproj` 364 registers the SinkGuard partial. Build in the cycle: analyzer and TreatWarningsAsErrors rebuilds exit 0 with `CSC_OUT_TASKMASTER 2` and `CSC_OUT_TASKMASTER_TEST 2`; the post-cycle coverage document carries one `` node per production file (COORD-CLASS-NODES 3). | +| AC7 | PASS | Each named comment re-read against the code (unchanged in the cycle): `HandleToggleClickAsync` summary (main 160-164) and remarks (170-183); `StartObservedPrime` remarks (Prime.cs 71-82); `CompletePrime` summary (131-137) and remarks (157-162); constructor docs for `enginesAccessor` (87-93) and `notifyUnavailable` (98-103). Two catch clauses exist (main 208, 295), so every count is correct. The retired phrases have 0 hits (reviewer Grep). | +| AC8 | PASS | Re-satisfied inside the cycle: `evidence/qa-gates/cycle1-toolchain-pass.md`, pass 1 clean. Step 1: `csharpier format .` left the SinkGuard hash unchanged, `csharpier check .` exit 0, 1640 files. Step 2: analyzer `/t:Rebuild` with `EnableNETAnalyzers` and `EnforceCodeStyleInBuild`, exit 0, 0 errors, 0 warnings, `CSC_OUT 2/2`. Step 3: `/t:Rebuild` with `TreatWarningsAsErrors=true` and no `/p:Nullable=enable`, exit 0, 0 errors, 0 warnings. Step 4: `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`, runner exit 0, 7390/7390 (item baseline 7384/7384; the six additions are the four first-cycle tests and the two cycle-1 data rows), `FINAL-FAILED-FQN-COUNT 0`, LINE-FLOOR and BRANCH-FLOOR MET (85.97% / 80.13%). Coordinator line coverage 100% at every stage (177/177 at baseline, 203/203 before and after the cycle), re-read at the class nodes of both raw documents; branches 44/44 after the cycle. | + +Totals: 8 PASS, 0 PARTIAL, 0 FAIL, 0 UNVERIFIED. + +### Remediation findings of cycle 1 (from `remediation-inputs.2026-10-03T08-43.md`) + +| ID | Required outcome | Status | Evidence | +|---|---|---|---| +| R-1 (from CR-1) | A deterministic MSTest test in the SinkGuard partial covering null and empty engine names on the refusal path: no throw, one notification containing `(null)`, no error, no engine member, no invalidation; the Messages class node reaches branch-rate 1; no production file changes | CLOSED | `SinkGuard.cs` 118-155 (`[DataTestMethod]`, `[DataRow(null)]`, `[DataRow("")]`, the five assertions); `cycle1-fixture-run.md` rows=2 passed=2; `coverage/remediation-964.cobertura.xml` line 230924 `branch-rate="1"` against `0.5` in `coverage/final-964.cobertura.xml` at the same line; COORD-BRANCHES 44/44; `cycle1-footprint.md` listing 3: test file only | +| R-2 (from CR-4) | Both symmetry assertions added to the both-sinks-throw test; the test continues to pass | CLOSED | `SinkGuard.cs` 112-113; `cycle1-r2-edit.md` R2-PLACEMENT 112 and 113; `cycle1-sinkguard-diff.md` 0 deleted lines; `cycle1-fixture-run.md` rows=1 passed=1 | +| Constraints | Test-only change set; SinkGuard at most 500 lines; MSTest, Moq, FluentAssertions; no temporary files or sleeps; full toolchain in order; evidence under FEATURE/evidence only; no AC edited | MET | `cycle1-footprint.md`; `cycle1-line-counts.md` (210); reviewer Read and Grep of the partial; `cycle1-toolchain-pass.md`; Glob of the feature folder; `scope-and-anchor.md` ISSUE-GREP-COUNTS | + +## Acceptance Criteria Check-off + +All eight criteria were already checked `[x]` by the first-cycle executor (P2-T10 to P2-T17, one flip per task, criterion text unmodified) and remained checked through the cycle (the cycle edited no line of `issue.md`). This review evaluated every criterion PASS, so no checkbox was changed and none was unchecked. Newly checked off by this review: none. + +### Acceptance Criteria Status +- Source: docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md +- Total AC items: 8 +- Checked off (delivered): 8 +- Remaining (unchecked): 0 +- Items remaining: none + +## Summary + +**Verdict: PASS.** 8/8 acceptance criteria met on evidence at head `01dcbe119`; R-1 and R-2 closed on code and evidence; 0 Blocking findings across the three review artifacts; non-blocking: CR-2 and CR-3 (Informational, unchanged, no change recommended), CR-5 (Informational, new: the SinkGuard partial's type-level summary omits its new region) and P-1 (Minor, evidence provenance: fourteen cycle artifacts re-stamped from composed labels to one host-clock reading with an in-field note; disclosed, no figure affected); 7 observations (O-1 to O-7). Remediation inputs: not produced. + +Independent verification performed by this review (no command executed): the caller's verbatim diff at head read in full; the SinkGuard partial read in full; the coordinator main file re-read at the refusal path, click boundary and guard; the primary fixture re-read at the data-row precedent and the Harness; both raw Cobertura documents read at the root and at every coordinator `` node; the worktree reflog read to confirm head, cycle commits and commit times and to cross-check the executor's labels; the feature folder enumerated for file types; the catch-clause, sink-call-site, banned-API and retired-phrase censuses re-derived by Grep; every cycle evidence file read against the plan's acceptance clauses. + +### Related-defect dispositions (for the coordinator) + +| ID | Item | Related to this item | Disposition requested | +|---|---|---|---| +| CR-1 / R-1 | `RenderEngineName` null-or-empty arm untested | Yes | Closed in cycle 1; no action | +| CR-4 / R-2 | Both-sinks-throw test lacked two symmetry assertions | Yes | Closed in cycle 1; no action | +| CR-5 | SinkGuard partial's type-level `` (lines 9-19) does not mention the null-or-empty-key test | Yes (touched file, comment drift) | Non-blocking Informational; one-clause addition at the coordinator's discretion under the related-defect directive; no behaviour or coverage effect either way | +| P-1 | Fourteen cycle artifacts re-stamped from composed `Timestamp:` labels to one host-clock reading with an in-field note | Yes (this item's evidence) | Non-blocking Minor; no task owed; the composing pattern should not recur (executor memory) | +| O-1 | Primary fixture at 481/500 lines | Yes, compliant | No action in this item | +| O-6 | Remediation plan header `Status:` / `Last Updated:` not updated after execution; no cycle-1 preflight record under `evidence/other/` | Yes (feature folder) | Orchestrator may refresh the two header lines at its next phase-boundary commit; informational | + +### Unrelated defects + +None found. Pre-existing repository-level conditions (canonical C# coverage artifact path absent; `quality-tiers.yml` absent) are already tracked and are not defects of this item. From 63a5094caea2e01c1279469f75e185fdfb9306c6 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Sat, 3 Oct 2026 09:54:13 -0400 Subject: [PATCH 17/17] docs(964): close re-audit findings CR-5 and O-6 (minimal cycle 2) Name the null-or-empty engine-key refusal-path test in the SinkGuard partial summary and correct the cycle 1 plan status header. Comment and documentation only; csharpier check on the touched file exits 0. Co-Authored-By: Claude Opus 5.5 --- ...neToggleStateCoordinatorTests.SinkGuard.cs | 6 +++-- .../other/cycle2-minimal-fix-record.md | 23 +++++++++++++++++++ .../remediation-plan.2026-10-03T08-43.md | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle2-minimal-fix-record.md diff --git a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs index 0dfb38e4b..910b7f41e 100644 --- a/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +++ b/TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs @@ -10,8 +10,10 @@ namespace TaskMaster.Test.Ribbon /// Regression tests for issue #964: on the refusal path of HandleToggleClickAsync, /// taken when the engines accessor yields null, a notifyUnavailable sink that throws /// must not escape into the async void Office handler, and its exception must be - /// reported once through logError; plus a guard for the issue #948 record placement - /// now that every sink call goes through one shared guard. A further partial of the + /// reported once through logError; a data-driven refusal-path test for a null or + /// empty engine key, which must render the null-name token in the single notification; plus + /// a guard for the issue #948 record placement now that every sink call goes through one + /// shared guard. A further partial of the /// coordinator fixture, so the private Harness and LoggedError types and the /// fixture constants are reused. The harness invokes OnNotify and OnLogError /// after it has recorded the call, so a throwing hook both records the attempt and models a diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle2-minimal-fix-record.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle2-minimal-fix-record.md new file mode 100644 index 000000000..1f353d503 --- /dev/null +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/evidence/other/cycle2-minimal-fix-record.md @@ -0,0 +1,23 @@ +# Remediation Cycle 2 - Minimal Fix Record (issue 964) + +Timestamp: 2026-10-03T09-53 +Command: dotnet tool run csharpier check TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs +EXIT_CODE: 0 +Output Summary: Checked 1 files in 460ms. (no formatting difference reported) + +## Authority and scope + +The coordinator (main session) ruled that the cycle 1 re-audit findings CR-5 and O-6 are related findings that the related-defect rule requires to be fixed inside this item. It directed a minimal cycle 2 record: apply both edits directly, without a re-plan or preflight, and run only the formatter check on the touched C# file. The orchestrator applied both edits itself under that ruling. No production file changed. + +Source review: code-review.2026-10-03T09-50.md (PASS, 0 blocking). + +## Findings closed + +- CR-5 (Informational, related): the type-level summary of `TaskMaster.Test/Ribbon/EngineToggleStateCoordinatorTests.SinkGuard.cs` did not mention the null-or-empty engine-key region added in cycle 1. The summary now names the data-driven refusal-path test for a null or empty engine key and its expected null-name token in the single notification. Comment-only change; no test code changed. +- O-6 (observation, related): the header of `remediation-plan.2026-10-03T08-43.md` read `Status: Authored, awaiting preflight` with all 33 tasks checked. It now records the executed state: preflight cleared in two rounds, all tasks checked, and the cycle 1 exit re-audit PASS. + +## Verification + +- Formatter check on the touched C# file: exit 0 (above). +- Change footprint against the cycle 2 base d5ff30136: exactly the two files above, 5 insertions and 3 deletions (`git diff --stat`). +- Not re-run, per the ruling: analyzer and type-check rebuilds, and the test-and-coverage run. The C# edit is confined to an XML documentation comment, so it cannot change compiled behaviour or coverage. Branch CI runs the full gate set on the PR head. diff --git a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md index 8444e26c1..c761e35d3 100644 --- a/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md +++ b/docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/remediation-plan.2026-10-03T08-43.md @@ -5,7 +5,7 @@ - **Owner:** drmoisan - **Work Mode:** minor-audit (`docs/features/active/2026-10-01-engine-toggle-coordinator-947-review-residuals-964/issue.md` line 12 reads `- Work Mode: minor-audit`) - **Last Updated:** 2026-10-03T08-43 -- **Status:** Authored, awaiting preflight +- **Status:** Executed. Preflight cleared in two rounds (round 1 REVISIONS REQUIRED, one P2-T11 delta applied verbatim; round 2 PREFLIGHT: ALL CLEAR); all 33 tasks checked; cycle 1 exit re-audit 2026-10-03T09-50 PASS with 0 blocking findings - **Version:** 1.0 (initial authoring of remediation cycle 1) - **Task counts (mechanical):** Phase 0 has 9 tasks (P0-T1 to P0-T9), Phase 1 has 8 (P1-T1 to P1-T8), Phase 2 has 16 (P2-T1 to P2-T16); 33 in total. - **Plan path continuity:** this file is updated in place for every revision round. No timestamped sibling plan file is created for this cycle. The executed plan `plan.2026-10-02T05-20.md` is read-only context and is not edited.