diff --git a/.claude/agent-memory/orchestrator/MEMORY.md b/.claude/agent-memory/orchestrator/MEMORY.md index 121285951..4afe8bca7 100644 --- a/.claude/agent-memory/orchestrator/MEMORY.md +++ b/.claude/agent-memory/orchestrator/MEMORY.md @@ -135,6 +135,7 @@ - [#751: 5-round preflight for novel infra](project_issue_751_five_round_preflight_detached_launch_convention.md) - budget more rounds when a plan invents execution infra - [A delegate may have no Bash tool](delegate-may-lack-bash-tool-verify-its-git-claims.md) - it cannot verify its own git claims; check them yourself - [Coverage seam workaround for .claude worktrees](coverage-seam-workaround-for-claude-worktrees.md) - dot-source TWO files, explicit -TestAssembly +- [PoshQC gates for scripts/hygiene](poshqc-gates-observed-outputs-for-scripts-hygiene.md) - summary has no numbers; junit is the observable; coverage doc omits scripts/hygiene; scope analyze - [isolation worktree spawn param kills the toolchain](isolation-worktree-spawn-param-kills-toolchain.md) - pwsh refused; isolation-dependent, NOT agent-type dependent - [An analyzer control site can be UNCOMPILED](analyzer-control-site-can-be-uncompiled-not-just-commented.md) - legacy csproj have explicit Compile items, no wildcard; verify live code AND a Compile Include entry - [Don't elect reviewer-declined optional changes](do-not-elect-reviewer-declined-optional-changes.md) - the substitute value was itself a defect; cost 2 rounds. Bar additive edits to keep a delta narrow diff --git a/.claude/agent-memory/orchestrator/poshqc-gates-observed-outputs-for-scripts-hygiene.md b/.claude/agent-memory/orchestrator/poshqc-gates-observed-outputs-for-scripts-hygiene.md new file mode 100644 index 000000000..f4006893f --- /dev/null +++ b/.claude/agent-memory/orchestrator/poshqc-gates-observed-outputs-for-scripts-hygiene.md @@ -0,0 +1,17 @@ +--- +name: poshqc-gates-observed-outputs-for-scripts-hygiene +description: What the PoshQC MCP tools actually return for scripts/hygiene work (observed 2026-10-02, issue 961 prep): no numbers in the summary, coverage doc omits scripts/hygiene, whole-repo analyze already fails, junit is the test observable +metadata: + type: project +--- + +Observed while preparing issue 961 (hygiene-guard backup-file rule), read-only runs against a clean tree: + +- `run_poshqc_test` / `_format` return only `ok:true` plus one sentence, no counts. The test observable is `artifacts/pester/pester-junit.xml` (root `tests`, `errors`, `failures`; testcase names are `.`). Baseline for `tests/scripts/hygiene` is 31 tests (7 + 19 + 5), 0 failures. The file is git-ignored, so Grep needs the explicit path. +- `artifacts/pester/powershell-coverage.xml` from the PoshQC test run does NOT contain `scripts/hygiene` in its denominator (only `.claude` and `.codex` packages). A hygiene line-coverage figure cannot be read locally; CI `_pester.yml` asserts LINE at 80 over `scripts/hygiene`. Do not write a local coverage-percentage acceptance for that folder. +- `run_poshqc_analyze` with no `scan_folders` fails with 21 pre-existing PSScriptAnalyzer issues; scoped to `["scripts/hygiene", "tests/scripts/hygiene"]` it returns ok. Always scope analyze and format. +- Format is a write-mode tool: observe it by a before/after `git hash-object` of the files, not by the tool status (plan-gate rule G7). +- The hygiene guard itself flags drive-letter user-profile paths in any tracked file outside `.claude/`, so plans and evidence for this work must use a `` placeholder and never the real path. +- A worktree-isolated shell refuses every `pwsh` form, so guard-run tasks need a non-isolated executor; the orchestrator itself can still call the PoshQC MCP tools. + +**How to apply:** when planning or preflighting work under `scripts/hygiene` or other folders outside the PoshQC coverage scope, scope the MCP calls, observe junit counts, and record coverage as CI-measured. See [[preflight-without-build-access-cannot-clear-a-plan]] and [[worktree-isolation-blocks-pwsh-per-agent-type]]. diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 0a4347b07..35d56e313 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -21,7 +21,7 @@ references each gate with `uses:`. It contains no inline `steps:`. | `_build-nullable.yml` | `windows-latest` | `msbuild /t:Rebuild` with `TreatWarningsAsErrors` | 30 min | | `_mstest-coverage.yml` | `windows-latest` | Plain `msbuild /t:Build`, then `scripts/vscode/Invoke-MSTestWithCoverage.ps1`, which runs the suite under `dotnet-coverage`, post-processes the result into a first-party Cobertura projection, and asserts 80% line and 75% branch against it; uploads the Cobertura document as the `test-results` artifact | 30 min | | `_pester.yml` | `windows-latest` | Pester over `tests/scripts/dependencies`, `tests/scripts/hygiene` and `tests/scripts/vscode` with JaCoCo coverage scoped to `scripts/dependencies`, `scripts/hygiene` and `scripts/vscode`; asserts the `LINE` figure at 80% and exits non-zero on any test failure; uploads the JaCoCo document as the `pester-coverage` artifact | 10 min | -| `_hygiene.yml` | `ubuntu-latest` | Runs scripts/hygiene/Test-RepositoryHygiene.ps1 over the tracked tree: fails on a raw test-platform or coverage-collector document (classified by content) or on a Windows user-profile path pattern in any tracked file outside .claude; prints path and line only | 10 min | +| `_hygiene.yml` | `ubuntu-latest` | Runs scripts/hygiene/Test-RepositoryHygiene.ps1 over the tracked tree: fails on a raw test-platform or coverage-collector document (classified by content), on a tracked backup file whose final extension is .bak (case-insensitive, finding line `HYGIENE backup-file `) or on a Windows user-profile path pattern in any tracked file outside .claude; prints the rule name, the path and, for a profile path, a line number only | 10 min | Structural properties that are deliberate and should not be changed casually: diff --git a/.gitignore b/.gitignore index 9f3f9cbee..505c6e1fe 100644 --- a/.gitignore +++ b/.gitignore @@ -254,8 +254,7 @@ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ -*.rptproj.bak -*.csproj.bak +*.bak # SQL Server files *.mdf diff --git a/TaskMaster.sln.bak b/TaskMaster.sln.bak deleted file mode 100644 index 050ab77e9..000000000 --- a/TaskMaster.sln.bak +++ /dev/null @@ -1,236 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32602.215 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "UtilitiesVB", "UtilitiesVB\UtilitiesVB.vbproj", "{2090BE3A-1105-4900-A481-EC1795A32BC0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tags", "Tags\Tags.csproj", "{FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToDoModel", "ToDoModel\ToDoModel.csproj", "{238EFD29-3CF3-07F3-28D3-C60D88F99769}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToDoModel.Test", "ToDoModel.Test\ToDoModel.Test.csproj", "{6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanProjectToTest", "CleanProjectToTest\CleanProjectToTest.csproj", "{6404299D-63F6-08A2-0115-8A4BBDAE587B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskMaster", "TaskMaster\TaskMaster.csproj", "{C70E9186-FAA0-0500-2770-A83EC81EC4E1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskVisualization", "TaskVisualization\TaskVisualization.csproj", "{7F3550A0-1128-0BE2-3020-EC4136E3F015}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskMaster.Test", "TaskMaster.Test\TaskMaster.Test.csproj", "{F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}" -EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "UtilitiesVB.Test", "UtilitiesVB.Test\UtilitiesVB.Test.vbproj", "{14090411-D873-4A08-81BD-E9BC9D9EA2CC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UtilitiesCS", "UtilitiesCS\UtilitiesCS.csproj", "{46628EC9-8A89-491E-84A2-6CF3069BA011}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UtilitiesCS.Test", "UtilitiesCS.Test\UtilitiesCS.Test.csproj", "{5DB8647F-4479-451F-B19A-10A4A0D62675}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{586F8EE3-DE4F-4787-9F8D-23B02BAEB8ED}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickFilerPort", "QuickFilerPort\QuickFilerPort.csproj", "{6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickFiler", "QuickFiler\QuickFiler.csproj", "{71735D4F-611A-4470-9170-4D9437534377}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickFilerVB.Test", "QuickFilerVB.Test\QuickFilerVB.Test.csproj", "{1857B6D0-0551-0E8C-05D2-48D27EDE26D6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickFiler.Test", "QuickFiler.Test\QuickFiler.Test.csproj", "{834DE5B0-03C8-4483-A4BC-A7975064C5A2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskVisualization.Test", "TaskVisualization.Test\TaskVisualization.Test.csproj", "{EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailIntelligence", "EmailIntelligence\EmailIntelligence.csproj", "{6B8006B4-9EB2-466E-A178-3E69FF42E16E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SVGControl", "SVGControl\SVGControl.csproj", "{D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SVGControl.Test", "SVGControl.Test\SVGControl.Test.csproj", "{13AC39E6-DE06-4337-8EB0-41CE674A4C3B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailIntelligence.Test", "EmailIntelligence.Test\EmailIntelligence.Test.csproj", "{9F8AA214-CE51-4140-A8ED-E909EE375E09}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskTree", "TaskTree\TaskTree.csproj", "{8F7F59E6-18A7-0CF3-0E1D-4478954B612A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Debug|x86.ActiveCfg = Debug|x86 - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Debug|x86.Build.0 = Debug|x86 - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Release|Any CPU.Build.0 = Release|Any CPU - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Release|x86.ActiveCfg = Release|Any CPU - {2090BE3A-1105-4900-A481-EC1795A32BC0}.Release|x86.Build.0 = Release|Any CPU - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Debug|x86.ActiveCfg = Debug|x86 - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Debug|x86.Build.0 = Debug|x86 - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Release|Any CPU.Build.0 = Release|Any CPU - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Release|x86.ActiveCfg = Release|Any CPU - {FFF74854-AD9D-023E-1A6A-EE7DBBD4AF72}.Release|x86.Build.0 = Release|Any CPU - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Debug|Any CPU.Build.0 = Debug|Any CPU - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Debug|x86.ActiveCfg = Debug|x86 - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Debug|x86.Build.0 = Debug|x86 - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Release|Any CPU.ActiveCfg = Release|Any CPU - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Release|Any CPU.Build.0 = Release|Any CPU - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Release|x86.ActiveCfg = Release|Any CPU - {238EFD29-3CF3-07F3-28D3-C60D88F99769}.Release|x86.Build.0 = Release|Any CPU - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Debug|x86.ActiveCfg = Debug|x86 - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Debug|x86.Build.0 = Debug|x86 - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Release|Any CPU.Build.0 = Release|Any CPU - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Release|x86.ActiveCfg = Release|Any CPU - {6EE2B4F0-28FA-47AE-8E0C-8B71376E798D}.Release|x86.Build.0 = Release|Any CPU - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Debug|x86.ActiveCfg = Debug|x86 - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Debug|x86.Build.0 = Debug|x86 - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Release|Any CPU.Build.0 = Release|Any CPU - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Release|x86.ActiveCfg = Release|Any CPU - {6404299D-63F6-08A2-0115-8A4BBDAE587B}.Release|x86.Build.0 = Release|Any CPU - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Debug|x86.ActiveCfg = Debug|x86 - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Debug|x86.Build.0 = Debug|x86 - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Release|Any CPU.Build.0 = Release|Any CPU - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Release|x86.ActiveCfg = Release|Any CPU - {C70E9186-FAA0-0500-2770-A83EC81EC4E1}.Release|x86.Build.0 = Release|Any CPU - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Debug|x86.ActiveCfg = Debug|x86 - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Debug|x86.Build.0 = Debug|x86 - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Release|Any CPU.Build.0 = Release|Any CPU - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Release|x86.ActiveCfg = Release|Any CPU - {7F3550A0-1128-0BE2-3020-EC4136E3F015}.Release|x86.Build.0 = Release|Any CPU - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Debug|x86.ActiveCfg = Debug|x86 - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Debug|x86.Build.0 = Debug|x86 - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Release|Any CPU.Build.0 = Release|Any CPU - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Release|x86.ActiveCfg = Release|Any CPU - {F535E5EC-CFBC-40F4-AB2E-7159F53FDB44}.Release|x86.Build.0 = Release|Any CPU - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Debug|x86.ActiveCfg = Debug|x86 - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Debug|x86.Build.0 = Debug|x86 - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Release|Any CPU.Build.0 = Release|Any CPU - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Release|x86.ActiveCfg = Release|Any CPU - {14090411-D873-4A08-81BD-E9BC9D9EA2CC}.Release|x86.Build.0 = Release|Any CPU - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Debug|Any CPU.Build.0 = Debug|Any CPU - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Debug|x86.ActiveCfg = Debug|x86 - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Debug|x86.Build.0 = Debug|x86 - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Release|Any CPU.ActiveCfg = Release|Any CPU - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Release|Any CPU.Build.0 = Release|Any CPU - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Release|x86.ActiveCfg = Release|Any CPU - {46628EC9-8A89-491E-84A2-6CF3069BA011}.Release|x86.Build.0 = Release|Any CPU - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Debug|x86.ActiveCfg = Debug|x86 - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Debug|x86.Build.0 = Debug|x86 - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Release|Any CPU.Build.0 = Release|Any CPU - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Release|x86.ActiveCfg = Release|Any CPU - {5DB8647F-4479-451F-B19A-10A4A0D62675}.Release|x86.Build.0 = Release|Any CPU - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Debug|x86.ActiveCfg = Debug|x86 - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Debug|x86.Build.0 = Debug|x86 - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Release|Any CPU.Build.0 = Release|Any CPU - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Release|x86.ActiveCfg = Release|Any CPU - {6FCA0845-4C4B-0CBA-217F-7F3C4FB5DFD5}.Release|x86.Build.0 = Release|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Debug|Any CPU.Build.0 = Debug|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Debug|x86.ActiveCfg = Debug|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Debug|x86.Build.0 = Debug|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Release|Any CPU.ActiveCfg = Release|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Release|Any CPU.Build.0 = Release|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Release|x86.ActiveCfg = Release|Any CPU - {71735D4F-611A-4470-9170-4D9437534377}.Release|x86.Build.0 = Release|Any CPU - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Debug|x86.ActiveCfg = Debug|x86 - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Debug|x86.Build.0 = Debug|x86 - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Release|Any CPU.Build.0 = Release|Any CPU - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Release|x86.ActiveCfg = Release|Any CPU - {1857B6D0-0551-0E8C-05D2-48D27EDE26D6}.Release|x86.Build.0 = Release|Any CPU - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Debug|x86.ActiveCfg = Debug|x86 - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Debug|x86.Build.0 = Debug|x86 - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Release|Any CPU.Build.0 = Release|Any CPU - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Release|x86.ActiveCfg = Release|Any CPU - {834DE5B0-03C8-4483-A4BC-A7975064C5A2}.Release|x86.Build.0 = Release|Any CPU - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Debug|x86.ActiveCfg = Debug|x86 - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Debug|x86.Build.0 = Debug|x86 - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Release|Any CPU.Build.0 = Release|Any CPU - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Release|x86.ActiveCfg = Release|Any CPU - {EEDAFFF0-EC43-4B95-A10B-27A9CC3C3667}.Release|x86.Build.0 = Release|Any CPU - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Debug|x86.ActiveCfg = Debug|x86 - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Debug|x86.Build.0 = Debug|x86 - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Release|Any CPU.Build.0 = Release|Any CPU - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Release|x86.ActiveCfg = Release|Any CPU - {6B8006B4-9EB2-466E-A178-3E69FF42E16E}.Release|x86.Build.0 = Release|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Debug|x86.ActiveCfg = Debug|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Debug|x86.Build.0 = Debug|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Release|Any CPU.Build.0 = Release|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Release|x86.ActiveCfg = Release|Any CPU - {D0FEE0D9-901A-4FB8-97D1-96A8F634B83C}.Release|x86.Build.0 = Release|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Debug|x86.ActiveCfg = Debug|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Debug|x86.Build.0 = Debug|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Release|Any CPU.Build.0 = Release|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Release|x86.ActiveCfg = Release|Any CPU - {13AC39E6-DE06-4337-8EB0-41CE674A4C3B}.Release|x86.Build.0 = Release|Any CPU - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Debug|x86.ActiveCfg = Debug|x86 - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Debug|x86.Build.0 = Debug|x86 - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Release|Any CPU.Build.0 = Release|Any CPU - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Release|x86.ActiveCfg = Release|Any CPU - {9F8AA214-CE51-4140-A8ED-E909EE375E09}.Release|x86.Build.0 = Release|Any CPU - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Debug|x86.ActiveCfg = Debug|x86 - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Debug|x86.Build.0 = Debug|x86 - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Release|Any CPU.Build.0 = Release|Any CPU - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Release|x86.ActiveCfg = Release|Any CPU - {8F7F59E6-18A7-0CF3-0E1D-4478954B612A}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CDB30996-B15F-49B4-B690-E559D01EEE81} - EndGlobalSection -EndGlobal diff --git a/TaskTree/TaskTree.vbproj.bak b/TaskTree/TaskTree.vbproj.bak deleted file mode 100644 index bd502f65f..000000000 --- a/TaskTree/TaskTree.vbproj.bak +++ /dev/null @@ -1,150 +0,0 @@ - - - - - Debug - AnyCPU - {3D5BD88D-D4FF-4302-8C45-8E06F7DC558A} - Library - TaskTree - TaskTree - 512 - Windows - v4.7.2 - true - - - true - full - true - true - bin\Debug\ - TaskTree.xml - 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 - - - pdbonly - false - true - true - bin\Release\ - TaskTree.xml - 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 - - - On - - - Binary - - - Off - - - On - - - x86 - bin\x86\Debug\ - - - x86 - bin\x86\Release\ - - - - True - - - ..\packages\ObjectListView.Official.2.9.1\lib\net20\ObjectListView.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - True - Application.myapp - - - True - True - Resources.resx - - - True - Settings.settings - True - - - - TaskTreeForm.vb - - - Form - - - - - VbMyResourcesResXFileCodeGenerator - Resources.Designer.vb - My.Resources - Designer - - - TaskTreeForm.vb - - - - - - MyApplicationCodeGenerator - Application.Designer.vb - - - SettingsSingleFileGenerator - My - Settings.Designer.vb - - - - - - {4dd3c93f-61c5-4dcf-9832-2403d9439bd2} - Tags - - - {91aa7c42-f0ab-4802-aa8b-0c73ea6ea3c9} - ToDoModel - - - {46628ec9-8a89-491e-84a2-6cf3069ba011} - UtilitiesCS - - - {2090be3a-1105-4900-a481-ec1795a32bc0} - UtilitiesVB - - - - \ No newline at end of file diff --git a/TaskVisualization/TaskVisualization.vbproj.bak b/TaskVisualization/TaskVisualization.vbproj.bak deleted file mode 100644 index 16f6d097c..000000000 --- a/TaskVisualization/TaskVisualization.vbproj.bak +++ /dev/null @@ -1,153 +0,0 @@ - - - - - Debug - AnyCPU - {CD11D1CB-DD70-4413-B278-263F5474C4B5} - Library - TaskVisualization - TaskVisualization - 512 - Windows - v4.7.2 - true - - - true - full - true - true - bin\Debug\ - TaskVisualization.xml - 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 - - - pdbonly - false - true - true - bin\Release\ - TaskVisualization.xml - 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 - - - On - - - Binary - - - Off - - - On - - - x86 - bin\x86\Debug\ - - - x86 - bin\x86\Release\ - - - - True - - - ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll - - - ..\packages\ObjectListView.Official.2.9.1\lib\net20\ObjectListView.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - True - Application.myapp - - - True - True - Resources.resx - - - True - Settings.settings - True - - - - TaskViewer.vb - - - Form - - - - - VbMyResourcesResXFileCodeGenerator - Resources.Designer.vb - My.Resources - Designer - - - TaskViewer.vb - - - - - - MyApplicationCodeGenerator - Application.Designer.vb - - - SettingsSingleFileGenerator - My - Settings.Designer.vb - - - - - - {4dd3c93f-61c5-4dcf-9832-2403d9439bd2} - Tags - - - {91aa7c42-f0ab-4802-aa8b-0c73ea6ea3c9} - ToDoModel - - - {46628EC9-8A89-491E-84A2-6CF3069BA011} - UtilitiesCS - - - {2090be3a-1105-4900-a481-ec1795a32bc0} - UtilitiesVB - - - - \ No newline at end of file diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T05-30.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T05-30.md new file mode 100644 index 000000000..320a7dc23 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T05-30.md @@ -0,0 +1,54 @@ +# Code Review - Issue #961 (hygiene guard rule C and backup-file removal) + +- Timestamp: 2026-10-02T05-30 +- Base: 94287369908cc920b21b0e3256314f988ad7d2f5; Head: f93005d38288020a2b0ebcfd2a51c1d713f7a357 +- Files reviewed: `scripts/hygiene/Test-RepositoryHygiene.ps1`, `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`, `.gitignore`, `.github/workflows/README.md`, three deleted `.bak` files. + +## Executive Summary + +The change is small, cohesive and correct. `Test-BackupFilePath` compares the final extension of the path with `.bak` using `[System.IO.Path]::GetExtension(...) -ieq '.bak'`, which matches the requirement (case-insensitive, final extension only, directory segments and longer extensions excluded). The orchestrator adds the finding line before content scanning, so a backup file is still scanned for profile paths, and the governance skip still runs first. Output format and exit decision are unchanged apart from the new line kind. No blocking defect was found. Three low-severity observations and two follow-ups are recorded below. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Low | tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 | `It 'reports zero findings for backup-lookalike names'` | CR-1: five lookalike names are asserted in one `It`, which departs from the one-behavior-per-`It` rule; a failure would not name the offending path. | Optional: convert to `-ForEach` cases, or accept as is because the rule tests in the sibling file already cover each name individually. | Each name is separately proven by `Test-BackupFilePath` tests 5 to 9, so the bundled case adds integration confidence only; diagnosis cost is small. | Added lines in the Tests.ps1 diff; Rules.Tests.ps1 tests 5 to 10 | +| Low | tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 | Array-equality assertions in the four added tests | CR-2: `@($result.Lines) \| Should -Be @(...)` carries no `-Because`; in the lookalike and governance tests the `ExitCode` assertion is also without one. | Optional: add `-Because` to the Lines assertions for consistency with the rest of the file. | The expected arrays are self-describing and the neighbouring assertions carry reasons, so failure output is still actionable. | Tests.ps1 diff, lines 159 to 203 | +| Low | .gitignore | lines 257 to 259 | CR-3: `*.rptproj.bak` and `*.csproj.bak` are now redundant with `*.bak`. | Leave them; removal is unrelated churn and the narrower lines document the earlier #951 intent. | No functional effect. The ignore rule is also not a barrier to `git add -f`, which is why rule C exists. | `.gitignore` diff | +| Info | .claude/agent-memory/orchestrator/ | `MEMORY.md`, `poshqc-gates-observed-outputs-for-scripts-hygiene.md` | CR-4: two orchestrator memory files are part of the branch diff though not in the stated footprint. | Orchestrator to confirm they are intended for this pull request. | Governance-directory content is excluded by the guard and contains no host paths; scope creep is limited to agent memory. | `git diff --stat` for `.claude` | +| Info | scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 | `Test-BackupFilePath` | CR-5: a path whose final segment is `.bak` (for example `dir/.bak`) is reported as a backup file because `GetExtension` returns `.bak`. | Accept: it matches the stated rule and is covered by the bare dot-bak test. | A literal file named `.bak` is plausibly a backup artifact; no false-positive tracked file exists on the tree (guard run reports 0). | Rules.Tests.ps1 test 4; reviewer guard run | + +## Correctness Analysis + +- Case handling: `-ieq` makes `Notes.BAK` a match; test 3 asserts it. +- Directory segment: `docs/bak/notes.md` has final extension `.md`; test 5 asserts false. +- Longer extension and compound names: `notes.bakery` and `notes.bak.md` return false; tests 6 and 7. +- Ordering: the finding line is emitted before the content read, so an unreadable backup file yields both `backup-file` and `unreadable` lines and counts two findings, which is consistent with the existing one-finding-per-rule behavior. The existing rule ordering (raw-document, unreadable, profile-path) is preserved after the new line. +- Governance exclusion: `.claude/` records are dropped before the new `if`; the added test proves `.claude/agent-memory/notes.bak` yields zero findings. +- Cross-platform: the git listing uses forward slashes, and `GetExtension` treats both separators, so the `ubuntu-latest` job behaves the same as the local Windows run. +- Real-tree control: P1-T9 printed `HYGIENE backup-file` for the three files and `HYGIENE Findings=3` (exit 1) before deletion; P2-T17 and the reviewer re-run print `HYGIENE Findings=0` after deletion. + +## Design and Style + +- Single responsibility: predicate in the Rules file, orchestration in the main script; no new coupling. +- Documentation: comment-based help states synopsis, description, parameter and output; the header comment of the guard and the `.DESCRIPTION` were updated to name rule C. +- Naming follows the existing `Verb-Noun` and `HYGIENE ` conventions. +- File sizes: 93, 146, 204 and 288 lines; all below 500. +- No suppressions, no new dependencies, no mutable script-scope state in production code. + +## Documentation Review + +The `_hygiene.yml` README row now names the backup rule, the case-insensitive final-extension condition and the finding-line format, and keeps the output-restriction sentence accurate (rule name, path, and a line number for a profile path only). The wording matches the implemented behavior. + +## Follow-ups (not filed) + +1. Read the CI `_pester.yml` LINE figure and `pester-coverage` JaCoCo artifact for `scripts/hygiene` after the push and record it against the policy audit; a figure below 85% would convert the conditional coverage verdict to FAIL. +2. Optional hygiene: after the first green CI run, confirm the `hygiene / Repository hygiene guard` check reports `Findings=0` on the pull request head (the guard is wired in `ci.yml` and `_hygiene.yml` and was not changed). + +## Acceptance Criteria Inventory + +The AC source is the `## Acceptance Criteria` section of `issue.md` (work mode `minor-audit`): AC-1 to AC-7. Per-criterion evaluation is in the feature audit of the same timestamp. + +## Acceptance Criteria Evaluation + +All seven criteria were verified against the diff and evidence; AC-6 carries a CI-sourced coverage component that is pending. See `feature-audit.2026-10-02T05-30.md`. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T06-18.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T06-18.md new file mode 100644 index 000000000..ee29c6394 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/code-review.2026-10-02T06-18.md @@ -0,0 +1,70 @@ +# Code Review - Issue #961 (re-run after remediation Phase 3) + +- Timestamp: 2026-10-02T06-18 +- Base: 94287369908cc920b21b0e3256314f988ad7d2f5; Head: 03f4b37c110222149e8a3f12300de22b60594d4d +- Files reviewed: `scripts/hygiene/Test-RepositoryHygiene.ps1`, `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`, `.gitignore`, `.github/workflows/README.md`, three deleted `.bak` files. Supersedes `code-review.2026-10-02T05-30.md` for the final tree. + +## Executive Summary + +The three prior low findings are resolved. CR-1 is closed: the lookalike test is a single `-ForEach` `It` over five one-per-line hashtables, with assertions on lines, count and exit code, each carrying a `-Because` that interpolates the case name. CR-2 is closed: every `Should` assertion in both modified test files carries `-Because`. CR-3 is closed: `.gitignore` has one `*.bak` line in place of the two specific lines, and AC-2 still holds with discriminating controls. Production code is unchanged since the first review (diff from b00fd1f19 to HEAD touches only `.gitignore` and `Test-RepositoryHygiene.Tests.ps1`). No blocking finding and no new finding in touched files. Two Info items remain. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Info | .claude/agent-memory/orchestrator/ | `MEMORY.md`, `poshqc-gates-observed-outputs-for-scripts-hygiene.md` | CR-4 (carried, RELATED): two orchestrator memory files are in the branch diff outside the declared footprint. | Orchestrator confirms they belong in this pull request; no change required from this item. | Governance-directory content, excluded by the guard, no host paths. | `git diff --name-status origin/main...HEAD -- .claude` | +| Info | tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1 | 7 `Should` lines | CR-6 (UNRELATED): 8 of 15 assertions carry `-Because`; the file is not in the branch diff and the gap pre-dates this branch. | Not remediated here; report for filing only if the maintainer wants uniformity. | The defect is in an untouched file with a different subject (the Git adapter); it is not caused by and does not affect this change. | Grep counts: 15 `Should -` lines, 8 with `-Because` | + +Resolved from the prior review: CR-1 (Low), CR-2 (Low), CR-3 (Low). CR-5 (Info, bare `.bak` file name is a backup) remains accepted: it matches the stated rule and has a covering test. + +## Verification of the Specific Remediation Items + +### CR-1 - lookalike test split + +- `Test-RepositoryHygiene.Tests.ps1` lines 171 to 186: `It 'reports zero findings for the backup-lookalike name ' -ForEach @( ... )` with five hashtables on separate lines: `docs/bak/notes.md`, `notes.bakery`, `notes.bak.md`, `backup`, `docs/features/x/Makefile`. +- Three assertions per case: the exact `@($result.Lines) | Should -Be @('HYGIENE Findings=0')`, `FindingCount` 0, `ExitCode` 0. Each `-Because` is a double-quoted string that interpolates `$Name`, so a failure names the case. +- The five names match the five negative names covered at predicate level in the Rules tests; the old title no longer exists. +- Execution: P3-T21 junit shows `tests="49"`, five passed cases with expanded names (no unexpanded `` text), zero failures. +- Pester detail checked: `$Name` is resolved by `-ForEach` data in the `It` body; `BeforeEach` resets `$script:Content` and `$script:Listing` before each case, so cases are independent. + +### CR-2 - `-Because` coverage + +- Reviewer counts: `Should -` lines equal `Should -.*-Because` lines in `Test-RepositoryHygiene.Tests.ps1` (29, 29) and `Test-RepositoryHygiene.Rules.Tests.ps1` (31, 31). +- The four orchestration tests and the related siblings in that file carry reasons that state the expected behavior in terms of the rule, for example the governance test: the backup record is dropped, so only the zero-findings total is printed. +- The Rules test file is unchanged by Phase 3 and already complete. + +### CR-3 - `.gitignore` + +- Lines 254 to 257 now read `UpgradeLog*.XML`, `UpgradeLog*.htm`, `ServiceFabricBackup/`, `*.bak`. No `*.rptproj.bak` or `*.csproj.bak` remains. +- The line is one `*.bak` rule, so `*.sln.bak`, `*.vbproj.bak`, `*.csproj.bak` and `*.rptproj.bak` all remain covered; P3-T14 shows all three sampled names attributed to `.gitignore:257:*.bak`. +- Negative control present and discriminating: P3-T9 and P3-T10 (rule absent, `foo.bak` unmatched, exit 1); P3-T16 (`README.md` not ignored, exit 1); P3-T12 (restoration byte-identical by hash). Reviewer re-run of `git check-ignore -q TaskMaster.sln.bak` exits 0. +- Removal is safe for the redundancy claim: the two specific patterns are strict subsets of `*.bak`. + +## Correctness Analysis + +- Predicate and orchestration code are unchanged. `Test-BackupFilePath` uses `[System.IO.Path]::GetExtension($RelativePath) -ieq '.bak'`; the finding line is emitted after the `.claude/` prefix skip and before the content scan. +- Reviewer guard run on the final tree: `HYGIENE Findings=0`, exit 0. +- Test determinism: no clock, random, file system or process use in added or changed tests; the only seam is the mocked `Invoke-GitExe` with signature parity. + +## Design and Style + +- File sizes read from disk: 93, 146, 209, 288 lines; all under 500. +- Arrange-Act-Assert separation holds in the `-ForEach` test. +- Tonality: comments, `-Because` text and artifacts are factual; no humor, hyperbole or metaphor. +- No suppressions and no dependencies added. + +## Documentation Review + +The `_hygiene.yml` README row names the backup rule, the case-insensitive final-extension condition and the `HYGIENE backup-file ` line, and it is unchanged by Phase 3. Plan version 1.4 records the three statements Phase 3 supersedes (the `-ForEach` statement, the specific-lines decision, the 45-test count), so the plan does not contradict the final tree. + +## Follow-ups + +None required from this item. The only open item is the pull-request-time CI figure: the `_pester.yml` LINE figure for the current head is pending and is read by the orchestrator. The earlier head measured `scripts/hygiene` at 94.23 percent and production lines are unchanged since. + +## Acceptance Criteria Inventory + +AC source: `## Acceptance Criteria` in `issue.md` (work mode `minor-audit`): AC-1 to AC-7. + +## Acceptance Criteria Evaluation + +All seven criteria verified against the diff and evidence. See `feature-audit.2026-10-02T06-18.md`. diff --git a/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 b/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 new file mode 100644 index 000000000..a562e3b42 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C check-ignore -v --no-index -- TaskMaster.sln.bak TaskTree/TaskTree.vbproj.bak TaskVisualization/TaskVisualization.vbproj.bak +EXIT_CODE: 1 +ExpectedExitCode: 1 +Output Summary: Empty output and exit 1: no ignore rule covers the three names before the change (AC-2 fail-before). diff --git a/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 b/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 new file mode 100644 index 000000000..2e9e6f299 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C check-ignore -v --no-index -- QuickFiler/QuickFiler.csproj.bak +EXIT_CODE: 0 +Output Summary: Output begins `.gitignore:258:*.csproj.bak` (followed by the queried path), proving the P0-T10 command shape reports an ignore rule when one exists. diff --git a/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 b/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 new file mode 100644 index 000000000..02b96a5cc --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE' +EXIT_CODE: 0 +Output Summary: Exactly one line: `HYGIENE Findings=0`. AC-3 fail-before record: the three tracked backups from P0-T5 are present and the guard reports nothing. diff --git a/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 b/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 new file mode 100644 index 000000000..60656431d --- /dev/null +++ b/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 @@ -0,0 +1,18 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C hash-object -- ; mcp__drm-copilot__run_poshqc_format (workspace_root=, scan_folders=["scripts/hygiene","tests/scripts/hygiene"]); git -C hash-object -- +EXIT_CODE: 0 +Output Summary: Tool returned ok (summary: ran bundled PoshQC format with 2 selected scan folders). The Hash Before and Hash After blocks are identical, so the six files were already formatted. +Hash Before: +5f9bf9a170fc260293e498171cdb502ff52610a6 +a3ff6437a0fa6a37c2fc7341ad54638682ea927e +52f725ce5f3d9fc407d3d78821b7275faa4ebb93 +3ce0af1034d47ae2bf4d0cbf9e41fda06c66ef51 +3a1aeac8cdaae73e4c3a2d4edf08e4421c462c53 +c767558c41232b6b157f900f5f2e36e43ce0299f +Hash After: +5f9bf9a170fc260293e498171cdb502ff52610a6 +a3ff6437a0fa6a37c2fc7341ad54638682ea927e +52f725ce5f3d9fc407d3d78821b7275faa4ebb93 +3ce0af1034d47ae2bf4d0cbf9e41fda06c66ef51 +3a1aeac8cdaae73e4c3a2d4edf08e4421c462c53 +c767558c41232b6b157f900f5f2e36e43ce0299f diff --git a/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 b/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 new file mode 100644 index 000000000..c157912da --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: mcp__drm-copilot__run_poshqc_analyze (workspace_root=, scan_folders=["scripts/hygiene","tests/scripts/hygiene"]); git -C status --porcelain -- scripts/hygiene tests/scripts/hygiene +EXIT_CODE: 0 +Output Summary: PoshQC analyze: pass (0 findings); tool reports no count. Tool summary sentence, verbatim: "Ran bundled PoshQC analyze against '' with 2 selected scan folder(s)." Porcelain output: empty (analyze changed nothing). diff --git a/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 b/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 new file mode 100644 index 000000000..cc0d63e9c --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: mcp__drm-copilot__run_poshqc_test (workspace_root=, scan_folders=["tests/scripts/hygiene"]); Read lines 1 to 2 of artifacts/pester/pester-junit.xml +EXIT_CODE: 0 +Output Summary: Tool returned ok. junit root element: tests="31", errors="0", failures="0". diff --git a/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 b/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 new file mode 100644 index 000000000..2647d3896 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: Grep `scripts/hygiene` (count) in artifacts/pester/powershell-coverage.xml; Grep `) +EXIT_CODE: 0 +Output Summary: Section heading hits = 1. Unchecked AC lines = 7. spec.md, user-story.md, research.md files = 0 (feature folder holds issue.md, the plan, and evidence/other/preflight-clearance.2026-10-02T03-30.md only). diff --git a/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 b/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 new file mode 100644 index 000000000..3f211451f --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-08 +Command: git -C merge-base origin/main HEAD +EXIT_CODE: 0 +Output Summary: One 40-hex-digit line printed. BASE-SHA = 94287369908cc920b21b0e3256314f988ad7d2f5 (equals the origin/main tip merged by the orchestrator before execution). diff --git a/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 b/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 new file mode 100644 index 000000000..8eea66c67 --- /dev/null +++ b/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 @@ -0,0 +1,6 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C diff --name-status -- docs/features +EXIT_CODE: 0 +Output Summary: Four entries, all status A: the three active-folder files (issue.md, plan.2026-10-02T02-25.md, evidence/other/preflight-clearance.2026-10-02T03-30.md) and docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md. Paths not beginning with the active-folder prefix: docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md +Companion Command: git -C status --porcelain -- docs/features +Companion Output: ?? docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/ (the Phase 0 evidence directory written by this run; begins with the active-folder prefix) diff --git a/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 b/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 new file mode 100644 index 000000000..12d8147a4 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C ls-files -- "*.bak" +EXIT_CODE: 0 +Output Summary: Three tracked paths (AC-1 fail-before): TaskMaster.sln.bak, TaskTree/TaskTree.vbproj.bak, TaskVisualization/TaskVisualization.vbproj.bak diff --git a/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 b/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 new file mode 100644 index 000000000..2a07e0d19 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C ls-files -- ":(icase)*.bak" +EXIT_CODE: 0 +Output Summary: The same three paths as P0-T5 and no others: TaskMaster.sln.bak, TaskTree/TaskTree.vbproj.bak, TaskVisualization/TaskVisualization.vbproj.bak. No upper-case variant is tracked. diff --git a/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 b/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 new file mode 100644 index 000000000..eb9930cf4 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: Glob `**/*.bak` from +EXIT_CODE: 0 +Output Summary: Exactly three matches: TaskMaster.sln.bak, TaskTree/TaskTree.vbproj.bak, TaskVisualization/TaskVisualization.vbproj.bak. No other file. Positive control for the P2-T7 absence check. diff --git a/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 b/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 new file mode 100644 index 000000000..c16b0407e --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C grep -l -I -F ".bak" -- . ":(exclude)docs" ":(exclude).claude" +EXIT_CODE: 0 +Output Summary: Exactly two file names: .gitignore and UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs. No project, solution, script or workflow file reads the three backups. diff --git a/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 b/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 new file mode 100644 index 000000000..e50f5440b --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-11 +Command: git -C grep -l -I -F ".bak" -- .gitignore +EXIT_CODE: 0 +Output Summary: Single line `.gitignore`, proving the P0-T8 search shape can match. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/phase0-instructions-read.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/phase0-instructions-read.md new file mode 100644 index 000000000..8afa69785 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/phase0-instructions-read.md @@ -0,0 +1,9 @@ +Timestamp: 2026-10-02T05-08 +Policy Order: CLAUDE.md, .claude/rules/general-code-change.md, .claude/rules/general-unit-test.md, .claude/rules/powershell.md, .claude/rules/tonality.md + +Files read: +- CLAUDE.md +- .claude/rules/general-code-change.md +- .claude/rules/general-unit-test.md +- .claude/rules/powershell.md +- .claude/rules/tonality.md diff --git a/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 b/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 new file mode 100644 index 000000000..37af562d2 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T09-45 +Command: gh run view 36990562362 --repo drmoisan/TaskMaster --json headSha,status,conclusion,jobs; gh run view 36990562362 --repo drmoisan/TaskMaster --job 110785531196 --log (filtered for the PESTER and COVERAGE summary lines); gh run download 36990562362 --repo drmoisan/TaskMaster --name pester-coverage (JaCoCo artifact, read with the Grep tool; not committed) +EXIT_CODE: 0 +Output Summary: CI run 36990562362 on head b00fd1f19f14ba6d320c958918649348483a44c5 (pull request 975); all seven checks passed (actionlint, build-analyzers, build-nullable, format-check, hygiene, mstest-coverage, pester). Pester job: `PESTER Passed=407 Failed=0 Skipped=0 Total=407`; `COVERAGE LinePercent=94.64 Covered=1765 Total=1865` over scripts/dependencies, scripts/hygiene and scripts/vscode (the job fails below 80). The scripts/hygiene package in the pester-coverage JaCoCo artifact: 98 lines covered, 6 missed (94.23 percent). Per file: Test-RepositoryHygiene.Rules.ps1 32 of 32 (100 percent, the new Test-BackupFilePath function and its siblings); Test-RepositoryHygiene.ps1 32 of 35 (91.43 percent; the Invoke-RepositoryHygieneMain function that carries the new rule C statement is 28 of 28, and the 3 missed lines are in the script-level entry); Test-RepositoryHygiene.Git.ps1 34 of 37 (91.89 percent, file unchanged by this change). Every changed file meets the 80 percent line floor and the new function meets the 90 percent new-code target. This closes the CI-sourced component of AC-6 recorded as pending in the reduced audit. Raw coverage and test-platform documents were read and not committed. +Source: CI run 36990562362, head b00fd1f19f14ba6d320c958918649348483a44c5. diff --git a/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 b/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 new file mode 100644 index 000000000..2f284935d --- /dev/null +++ b/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 @@ -0,0 +1,21 @@ +Timestamp: 2026-10-02T05-12 + +The executor edits only the footprint paths. + +Declared footprint (from the plan Scope section, verbatim): + +- delete `TaskMaster.sln.bak` +- delete `TaskTree/TaskTree.vbproj.bak` +- delete `TaskVisualization/TaskVisualization.vbproj.bak` +- edit `.gitignore` +- edit `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1` +- edit `scripts/hygiene/Test-RepositoryHygiene.ps1` +- edit `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` +- edit `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` +- edit `.github/workflows/README.md` +- edit `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md` (AC check-off only) +- write the plan file and evidence files under `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/` + +Binding Bash discipline (from the plan execution rules, verbatim): + +Never use `cd`. Address the item worktree with `git -C ...` for every git command, and pass absolute paths to every other tool. Never invoke grep, sed, awk, cat, head, tail, find, cp, mv, rm or echo through Bash; use the Grep, Read, Glob, Edit and Write tools with absolute paths instead. The only Bash forms permitted are single commands (no `&&`, `;` or `|` chaining) whose first token is `git`, `pwsh` or `poetry`, plus the three `.claude/lib/bash/*.sh` scripts. If a shell step is genuinely needed, run it as one `pwsh -NoProfile -Command '...'` invocation with the worktree path inside the command string, never as a `cd`. If the shell refuses a `pwsh` or `git` form, stop and report BLOCKED; do not substitute another tool. diff --git a/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 b/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 new file mode 100644 index 000000000..995675bae --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-16 +Command: git -C rm -- TaskMaster.sln.bak TaskTree/TaskTree.vbproj.bak TaskVisualization/TaskVisualization.vbproj.bak +EXIT_CODE: 0 +Output Summary: Three lines, one per path: rm 'TaskMaster.sln.bak', rm 'TaskTree/TaskTree.vbproj.bak', rm 'TaskVisualization/TaskVisualization.vbproj.bak'. No other file removed. diff --git a/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 b/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 new file mode 100644 index 000000000..eef1ac9ad --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-16 +Command: Edit tool insert of the line `*.bak` after `*.csproj.bak` in .gitignore; Read .gitignore lines 255 to 261; Grep `^` (count); Grep `\r$` (count) +EXIT_CODE: 0 +Output Summary: Read shows `*.rptproj.bak` (257), `*.csproj.bak` (258) and `*.bak` (259) on consecutive lines. Line count = 370. CR-terminated line count = 370 (the inserted line carries CRLF; the Edit tool preserved the file's line endings). No `!` negation line added. diff --git a/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 b/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 new file mode 100644 index 000000000..1ab301a1f --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-16 +Command: Edit tool on the `_hygiene.yml` table row (line 24) of .github/workflows/README.md; Grep `_hygiene.yml.*HYGIENE backup-file` (count); Grep `fails on a raw test-platform` (count); Grep `^` (count); Grep `\r$` (count) +EXIT_CODE: 0 +Output Summary: `_hygiene.yml.*HYGIENE backup-file` hits = 1. `fails on a raw test-platform` hits = 1. Line count = 294 and CR-terminated line count = 294 (equal; CRLF endings preserved). diff --git a/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 b/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 new file mode 100644 index 000000000..2041a97f9 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-13 +Command: Edit tool append to tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1; Grep `^Describe 'Test-BackupFilePath' \{` (count); Grep `^\s*It ` (count); Grep `^` (count) +EXIT_CODE: 0 +Output Summary: Describe 'Test-BackupFilePath' hits = 1. It blocks = 29 (19 + 10). Line count = 288 (at most 500). diff --git a/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 b/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 new file mode 100644 index 000000000..2b94b6762 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-13 +Command: Edit tool add 4 It blocks to tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1; Grep `^\s*It ` (count); Grep alternation of the four exact It names (count); Grep `^` (count) +EXIT_CODE: 0 +Output Summary: It blocks = 11 (7 + 4). The four named It blocks matched = 4. Line count = 204 (at most 500). diff --git a/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 b/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 new file mode 100644 index 000000000..11cce5db1 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-14 +Command: Edit tool append of function Test-BackupFilePath to scripts/hygiene/Test-RepositoryHygiene.Rules.ps1; Grep `^function Test-BackupFilePath` (count); Grep `ieq '\.bak'` (count); Grep `^` (count) +EXIT_CODE: 0 +Output Summary: Function definition hits = 1. `ieq '.bak'` hits = 1. Line count = 146 (at most 500). diff --git a/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 b/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 new file mode 100644 index 000000000..363b55382 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-14 +Command: Edit tool insert in Invoke-RepositoryHygieneMain of scripts/hygiene/Test-RepositoryHygiene.ps1; Grep `Test-BackupFilePath -RelativePath` (count); Grep `HYGIENE backup-file` (count); Read of the function +EXIT_CODE: 0 +Output Summary: `Test-BackupFilePath -RelativePath` hits = 1. `HYGIENE backup-file` hits at this task = 1 (a second hit is added by P1-T7). Read of the function shows the three-line `if` after the governance `continue` block and before `$text = $null`, with no `continue` inside the new block. diff --git a/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 b/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 new file mode 100644 index 000000000..713d5cb46 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-14 +Command: Edit tool (header comment, .SYNOPSIS, .DESCRIPTION) in scripts/hygiene/Test-RepositoryHygiene.ps1; Grep `\(rule C\)` (count); Grep `HYGIENE backup-file` (count); Grep `both hygiene rules` (count); Grep `^` (count) +EXIT_CODE: 0 +Output Summary: `(rule C)` hits = 1. `HYGIENE backup-file` hits = 2 (code plus description). `both hygiene rules` hits = 0. Line count = 93 (at most 500). diff --git a/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 b/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 new file mode 100644 index 000000000..502a4cfec --- /dev/null +++ b/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 @@ -0,0 +1,53 @@ +Timestamp: 2026-10-02T05-20 + +Handoff to the orchestrator-owned reduced small-audit. The executor neither delegates nor runs the audit. Execution stops after this artifact. + +Artifacts written in P0-T1 through P2-T17 (repository-relative paths; F = docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961): + +F/evidence/baseline/phase0-instructions-read.md +F/evidence/baseline/p0-t2-feature-folder-preconditions.2026-10-02T05-08.md +F/evidence/baseline/p0-t3-base-sha.2026-10-02T05-08.md +F/evidence/baseline/p0-t4-carried-docs.2026-10-02T05-11.md +F/evidence/baseline/p0-t5-ac1-baseline.2026-10-02T05-11.md +F/evidence/baseline/p0-t6-icase-inventory.2026-10-02T05-11.md +F/evidence/baseline/p0-t7-worktree-inventory.2026-10-02T05-11.md +F/evidence/baseline/p0-t8-no-reader-search.2026-10-02T05-11.md +F/evidence/baseline/p0-t9-search-control.2026-10-02T05-11.md +F/evidence/baseline/p0-t10-ac2-baseline.2026-10-02T05-11.md +F/evidence/baseline/p0-t11-check-ignore-control.2026-10-02T05-11.md +F/evidence/baseline/p0-t12-guard-baseline.2026-10-02T05-11.md +F/evidence/baseline/p0-t13-format-baseline.2026-10-02T05-11.md +F/evidence/baseline/p0-t14-analyze-baseline.2026-10-02T05-11.md +F/evidence/baseline/p0-t15-test-baseline.2026-10-02T05-11.md +F/evidence/baseline/p0-t16-coverage-limitation.2026-10-02T05-11.md +F/evidence/other/p1-t1-implementation-handoff.2026-10-02T05-12.md +F/evidence/other/p1-t2-rule-tests-added.2026-10-02T05-13.md +F/evidence/other/p1-t3-orchestration-tests-added.2026-10-02T05-13.md +F/evidence/regression-testing/p1-t4-expect-fail-test-run.2026-10-02T05-13.md +F/evidence/other/p1-t5-rule-function-added.2026-10-02T05-14.md +F/evidence/other/p1-t6-guard-rule-wired.2026-10-02T05-14.md +F/evidence/other/p1-t7-guard-docs-updated.2026-10-02T05-14.md +F/evidence/regression-testing/p1-t8-test-run-pass.2026-10-02T05-14.md +F/evidence/regression-testing/p1-t9-guard-negative-control.2026-10-02T05-14.md +F/evidence/other/p1-t10-git-rm.2026-10-02T05-16.md +F/evidence/other/p1-t11-gitignore-edit.2026-10-02T05-16.md +F/evidence/other/p1-t12-readme-row-edit.2026-10-02T05-16.md +F/evidence/qa-gates/p2-t1-format.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t2-analyze.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t3-test.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t4-statement-coverage-map.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t5-file-sizes.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t6-ac1-index.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t7-ac1-worktree-absence.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t8-ac2-check-ignore-q.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t9-ac2-check-ignore-v.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t10-ac2-negative-control.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t11-ac2-exact-line.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t12-ac7-readme-row.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t13-stage-footprint.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t14-base-continuity.2026-10-02T05-17.md +F/evidence/qa-gates/p2-t15-footprint-non-docs.2026-10-02T05-18.md +F/evidence/qa-gates/p2-t16-footprint-feature-folder.2026-10-02T05-18.md +F/evidence/qa-gates/p2-t17-guard-final.2026-10-02T05-18.md + +Open item for the audit: PoshQC reports no scripts/hygiene line coverage; CI (_pester.yml, LINE at 80) is the coverage source and is read by the orchestrator after the push. diff --git a/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 b/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 new file mode 100644 index 000000000..a048f4a3f --- /dev/null +++ b/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 @@ -0,0 +1,21 @@ +Timestamp: 2026-10-02T06-07 +Command: Write tool (handoff record; no shell command) +EXIT_CODE: 0 +Output Summary: Phase 3 remediation handoff recorded. + +# Phase 3 remediation handoff (P3-T1) + +Phase 3 footprint: + +- tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 +- .gitignore +- docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/plan.2026-10-02T02-25.md +- the Phase 3 evidence files under docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/ + +tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 is READ-ONLY in Phase 3 and is not in the edit footprint. + +Binding Bash discipline: never use cd; address the item worktree with git -C for every git command and absolute paths for every other tool; never invoke grep, sed, awk, cat, head, tail, find, cp, mv, rm or echo through Bash; only single commands (no chaining) whose first token is git, gh, pwsh, poetry, dotnet or msbuild are permitted; a needed shell step runs as one pwsh -NoProfile -Command invocation. + +PowerShell batch budget: Phase 3 edits only tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 among PowerShell files and creates no PowerShell file. Nothing under .claude/state/ is touched. On any denied PowerShell write the executor stops, commits, pushes and reports. + +No other file is touched and issue.md is not edited in Phase 3. diff --git a/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 b/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 new file mode 100644 index 000000000..7dcf0bc3a --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-11 +Command: Edit tool deleting `*.rptproj.bak` and `*.csproj.bak` from .gitignore; Read of .gitignore lines 254 to 259; Grep counts of `^\*\.(rptproj|csproj)\.bak`, `^\*\.bak`, `^` and `\r$` on .gitignore +EXIT_CODE: 0 +Output Summary: Lines 255 to 258 read `UpgradeLog*.htm`, `ServiceFabricBackup/`, `*.bak` and a blank line. Grep `^\*\.(rptproj|csproj)\.bak` count 0; `^\*\.bak` count 1; `^` count 368 (P3-T2 count 370 minus 2); `\r$` count 368 (equal, CRLF preserved). No negation line added. diff --git a/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 b/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 new file mode 100644 index 000000000..fe7d70aa9 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-11 +Command: Grep files-with-matches of `\.gitignore:25[0-9]` over /evidence with glob `p[02]-t*.md`; Grep count of `^- \[x\] AC-2:` and of `^- \[x\] AC-` in issue.md +EXIT_CODE: 0 +Output Summary: Files citing a .gitignore line number are exactly p0-t11-check-ignore-control.2026-10-02T05-11.md (line 258), p2-t9-ac2-check-ignore-v.2026-10-02T05-17.md and p2-t11-ac2-exact-line.2026-10-02T05-17.md (line 259). AC-2 count 1; AC- count 7. The three files are historical records of the Phase 0 and 2 trees and are not edited; P3-T14 and P3-T17 supersede their line numbers (257). The orchestrator-owned feature-audit file cites line 259 in its AC-2 row, lies outside the scanned folder and is not edited. diff --git a/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 b/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 new file mode 100644 index 000000000..b58420fef --- /dev/null +++ b/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 @@ -0,0 +1,32 @@ +Timestamp: 2026-10-02T06-15 + +Artifacts written in P3-T1 through P3-T26 (repository-relative; F = docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961): + +- F/evidence/other/p3-t1-remediation-handoff.2026-10-02T06-07.md +- F/evidence/regression-testing/p3-t2-pre-remediation-fingerprints.2026-10-02T06-08.md +- F/evidence/regression-testing/p3-t3-pre-remediation-test-run.2026-10-02T06-08.md +- F/evidence/regression-testing/p3-t4-pre-removal-check-ignore.2026-10-02T06-08.md +- F/evidence/other/p3-t5-cr1-lookalike-split.2026-10-02T06-09.md +- F/evidence/other/p3-t6-cr2-backup-test-because.2026-10-02T06-09.md +- F/evidence/other/p3-t7-cr2-sibling-because.2026-10-02T06-09.md +- F/evidence/qa-gates/p3-t8-cr2-rules-tests-because-sweep.2026-10-02T06-09.md +- F/evidence/regression-testing/p3-t9-control-covering-rule-removed.2026-10-02T06-10.md +- F/evidence/regression-testing/p3-t10-control-plain-bak-unignored.2026-10-02T06-10.md +- F/evidence/regression-testing/p3-t11-control-specific-lines-only.2026-10-02T06-10.md +- F/evidence/regression-testing/p3-t12-control-restored.2026-10-02T06-10.md +- F/evidence/other/p3-t13-gitignore-redundant-lines-removed.2026-10-02T06-11.md +- F/evidence/qa-gates/p3-t14-ac2-samples-after-removal.2026-10-02T06-11.md +- F/evidence/qa-gates/p3-t15-ac2-check-ignore-q.2026-10-02T06-11.md +- F/evidence/qa-gates/p3-t16-ac2-negative-control.2026-10-02T06-11.md +- F/evidence/qa-gates/p3-t17-ac2-exact-line.2026-10-02T06-11.md +- F/evidence/other/p3-t18-citation-scan.2026-10-02T06-11.md +- F/evidence/qa-gates/p3-t19-format.2026-10-02T06-12.md +- F/evidence/qa-gates/p3-t20-analyze.2026-10-02T06-12.md +- F/evidence/qa-gates/p3-t21-test.2026-10-02T06-12.md +- F/evidence/qa-gates/p3-t22-stage-footprint.2026-10-02T06-13.md +- F/evidence/qa-gates/p3-t23-guard-final.2026-10-02T06-13.md +- F/evidence/qa-gates/p3-t24-footprint-non-docs.2026-10-02T06-14.md +- F/evidence/qa-gates/p3-t25-footprint-feature-folder.2026-10-02T06-14.md +- F/evidence/qa-gates/p3-t26-base-continuity.2026-10-02T06-14.md + +The executor neither delegates nor runs the re-review, and no issue.md check-off changed in Phase 3. diff --git a/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 b/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 new file mode 100644 index 000000000..345cbd61e --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-09 +Command: Edit tool on tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 (single It replaced by one -ForEach It over five hashtables); Grep calls on that file +EXIT_CODE: 0 +Output Summary: Old title count 0; new title `reports zero findings for the backup-lookalike name` count 1; `-ForEach` count 1; `@{ Name = '` count 5; `^\s*It ` count 11; line count 209 (at most 500). diff --git a/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 b/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 new file mode 100644 index 000000000..1cc3b6a14 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-09 +Command: Edit tool (three edits) on tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1; Grep of the three -Because prefixes +EXIT_CODE: 0 +Output Summary: Combined Grep for `-Because 'one tracked backup file yields`, `-Because 'the governance-directory backup record is dropped` and `-Because 'a backup file that also carries a profile path` returns 3 matching lines, one per token (each located by its unique text or enclosing test title). diff --git a/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 b/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 new file mode 100644 index 000000000..05af84fbd --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-09 +Command: Edit tool (eight edits) on tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1; Grep count mode of `Should` and of `-Because` on that file +EXIT_CODE: 0 +Output Summary: `Should` lines 29 and `-Because` lines 29 (previously 29 and 16); the two counts are equal. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/preflight-clearance.2026-10-02T03-30.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/preflight-clearance.2026-10-02T03-30.md new file mode 100644 index 000000000..b089353e5 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/preflight-clearance.2026-10-02T03-30.md @@ -0,0 +1,23 @@ +# Preflight clearance (issue 961) + +Timestamp: 2026-10-02T03-30 + +PREFLIGHT: ALL CLEAR + +CONVERGENCE: NO FURTHER ROUNDS EXPECTED + +- Rounds: 3 +- Per-round defect counts: round 1 = 5 defects (D1 to D5), round 2 = 3 defects, round 3 = 0 defects +- Plan path: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/plan.2026-10-02T02-25.md` +- Plan version cleared: 1.2 +- Git blob SHA of the cleared plan: f7ecf98a3c023f176c30efe6f7ef9c8f8abf758c +- Plan validator (`validate_orchestration_artifacts`, artifact type `plan`): ok on the cleared text +- Work mode: minor-audit (acceptance criteria source: `issue.md`, `## Acceptance Criteria`, AC-1 to AC-7) + +Round summary: + +- Round 1 (REVISIONS REQUIRED): promoted record unaccounted for in the footprint task; moving `origin/main` ref used as the diff anchor; guard run claimed to scan unstaged evidence; non-isolated executor requirement for the `pwsh` guard tasks; unpinned `-ieq` token. +- Round 2 (REVISIONS REQUIRED): undetermined exit code for the expect-fail test run; clean-listing positive case of AC-4 without a mapped task; AC-5 check-off omitted the base-continuity artifact. +- Round 3 (ALL CLEAR): all tree facts re-derived by read-only commands; no remaining defect. + +Execution caveat recorded for the executor launch: tasks P0-T12, P1-T9 and P2-T17 run `pwsh` through the shell, which a worktree-isolated shell refuses, so the execution must run non-isolated. diff --git a/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 b/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 new file mode 100644 index 000000000..6fffea744 --- /dev/null +++ b/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 @@ -0,0 +1,19 @@ +Timestamp: 2026-10-02T05-17 +ITERATION: 1 +Command: git -C hash-object -- ; mcp__drm-copilot__run_poshqc_format (workspace_root=, scan_folders=["scripts/hygiene","tests/scripts/hygiene"]); git -C hash-object -- +EXIT_CODE: 0 +Output Summary: Tool returned ok (summary: ran bundled PoshQC format with 2 selected scan folders). The Hash Before and Hash After blocks are identical, so the formatter changed nothing. +Hash Before: +e36a55d866151444849e4c67686eb211b1788989 +a733b734b630e9f6184eb5818f5aeb0b337c90fa +52f725ce5f3d9fc407d3d78821b7275faa4ebb93 +bef00db2d06accf646e611816f8212ed4891ff19 +db1ded33017df6e04f1b0c42e400a6a1b98c64f5 +c767558c41232b6b157f900f5f2e36e43ce0299f +Hash After: +e36a55d866151444849e4c67686eb211b1788989 +a733b734b630e9f6184eb5818f5aeb0b337c90fa +52f725ce5f3d9fc407d3d78821b7275faa4ebb93 +bef00db2d06accf646e611816f8212ed4891ff19 +db1ded33017df6e04f1b0c42e400a6a1b98c64f5 +c767558c41232b6b157f900f5f2e36e43ce0299f diff --git a/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 b/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 new file mode 100644 index 000000000..d5653bf99 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T05-17 +Command: git -C check-ignore -q -- README.md +EXIT_CODE: 1 +ExpectedExitCode: 1 +Output Summary: Empty output and exit 1: the P2-T8 command shape returns non-zero for a path no rule ignores, so its exit 0 is discriminating. diff --git a/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 b/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 new file mode 100644 index 000000000..feceb6886 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-17 +Command: git -C grep -n -I -F "*.bak" -- .gitignore +EXIT_CODE: 0 +Output Summary: Exactly one line: `.gitignore:259:*.bak`. diff --git a/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 b/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 new file mode 100644 index 000000000..dbaca8cba --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-17 +Command: Grep `_hygiene.yml.*HYGIENE backup-file` (content mode) in .github/workflows/README.md +EXIT_CODE: 0 +Output Summary: Exactly one hit, on line 24 (the table row; zero hits before P1-T12). diff --git a/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 b/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 new file mode 100644 index 000000000..aa219f3aa --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-17 +Command: git -C add -- .gitignore .github/workflows/README.md scripts/hygiene/Test-RepositoryHygiene.ps1 scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961 +EXIT_CODE: 0 +Output Summary: No error output; only line-ending conversion warnings (LF to CRLF) for new evidence files. diff --git a/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 b/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 new file mode 100644 index 000000000..3b46924ad --- /dev/null +++ b/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 @@ -0,0 +1,7 @@ +Timestamp: 2026-10-02T05-17 +Command: git -C merge-base --is-ancestor HEAD +EXIT_CODE: 0 +Output Summary: BASE-SHA is an ancestor of HEAD (empty output, exit 0). +Command: git -C merge-base origin/main HEAD +EXIT_CODE: 0 +Output Summary: Printed 94287369908cc920b21b0e3256314f988ad7d2f5, equal to BASE-SHA recorded in P0-T3. Branch history was not rewritten. diff --git a/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 b/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 new file mode 100644 index 000000000..88b9e2e29 --- /dev/null +++ b/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 @@ -0,0 +1,6 @@ +Timestamp: 2026-10-02T05-18 +Command: git -C diff --name-status -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory" +EXIT_CODE: 0 +Output Summary: Exactly nine paths. D: TaskMaster.sln.bak, TaskTree/TaskTree.vbproj.bak, TaskVisualization/TaskVisualization.vbproj.bak. M: .gitignore, .github/workflows/README.md, scripts/hygiene/Test-RepositoryHygiene.ps1, scripts/hygiene/Test-RepositoryHygiene.Rules.ps1, tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1, tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1. The Git adapter scripts/hygiene/Test-RepositoryHygiene.Git.ps1 and its test file do not appear. +Companion Command: git -C status --porcelain -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory" +Companion Output: empty. No `??` entry and no other entry. Deviation from the plan wording ("the same nine entries"): the nine changes were committed and pushed at the Phase 1 boundary (commit cadence binding on the executor), so porcelain status, which lists only uncommitted changes, is empty. The anchored diff above is the authoritative nine-path listing; the empty porcelain output establishes that no untracked or uncommitted path exists outside the footprint. diff --git a/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 b/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 new file mode 100644 index 000000000..e4f1cd5c5 --- /dev/null +++ b/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 @@ -0,0 +1,6 @@ +Timestamp: 2026-10-02T05-18 +Command: git -C diff --name-status -- docs/features +EXIT_CODE: 0 +Output Summary: Every listed path has status A. All begin with docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/ (issue.md, the plan, and the evidence files of P0-T1 through P2-T12) except docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md (status A), which is exactly the single path recorded in P0-T4. No other path under docs/ is listed. +Companion Command: git -C status --porcelain -- docs/features +Companion Output: Entries are the two evidence files p2-t13-stage-footprint.2026-10-02T05-17.md and p2-t14-base-continuity.2026-10-02T05-17.md as untracked, and the P2-T1 to P2-T12 evidence files as staged additions; every entry begins with the active-folder prefix. diff --git a/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 b/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 new file mode 100644 index 000000000..bda696567 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-18 +Command: git -C add -- docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961; pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE' +EXIT_CODE: 0 +Output Summary: The git add printed no error (line-ending warnings only). Guard output is exactly one line: `HYGIENE Findings=0`. The same command printed `HYGIENE Findings=3` and exited 1 in P1-T9. diff --git a/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 b/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 new file mode 100644 index 000000000..1832d8135 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T05-17 +ITERATION: 1 +Command: mcp__drm-copilot__run_poshqc_analyze (workspace_root=, scan_folders=["scripts/hygiene","tests/scripts/hygiene"]); git -C hash-object -- +EXIT_CODE: 0 +Output Summary: PoshQC analyze: pass (0 findings); tool reports no count. Tool summary sentence, verbatim: "Ran bundled PoshQC analyze against '' with 2 selected scan folder(s)." The six hashes after analyze equal the Hash After block of the P2-T1 artifact of iteration 1 (e36a55d8..., a733b734..., 52f725ce..., bef00db2..., db1ded33..., c767558c...); analyze changed nothing. diff --git a/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 b/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 new file mode 100644 index 000000000..cf6ad39c9 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T05-17 +ITERATION: 1 +Command: mcp__drm-copilot__run_poshqc_test (workspace_root=, scan_folders=["tests/scripts/hygiene"]); Read lines 1 to 2 of artifacts/pester/pester-junit.xml; Grep `status="Failed"` (count) +EXIT_CODE: 0 +Output Summary: Tool returned ok. junit root: tests="45" (baseline 31), errors="0", failures="0". status="Failed" count = 0. diff --git a/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 b/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 new file mode 100644 index 000000000..5e2fef5c4 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T05-17 +Command: Grep ` ls-files -- "*.bak" +EXIT_CODE: 0 +Output Summary: Empty output (the same command printed three paths in P0-T5, its positive control). diff --git a/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 b/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 new file mode 100644 index 000000000..9b94804ad --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-17 +Command: Read /TaskMaster.sln.bak; Read /TaskTree/TaskTree.vbproj.bak; Read /TaskVisualization/TaskVisualization.vbproj.bak; Glob `**/*.bak` from ; Read /.gitignore (limit 1, control) +EXIT_CODE: 0 +Output Summary: Each of the three backup reads returned a file-not-found error. The glob returned no file (it returned three in P0-T7). The control read of .gitignore succeeded (line 1 returned). diff --git a/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 b/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 new file mode 100644 index 000000000..7e120be97 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-17 +Command: git -C check-ignore -q TaskMaster.sln.bak +EXIT_CODE: 0 +Output Summary: Empty output and exit 0: the path is ignored. diff --git a/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 b/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 new file mode 100644 index 000000000..ade7c3987 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T05-17 +Command: git -C check-ignore -v -- TaskMaster.sln.bak TaskTree/TaskTree.vbproj.bak TaskVisualization/TaskVisualization.vbproj.bak +EXIT_CODE: 0 +Output Summary: Three lines, one per path, each beginning `.gitignore:259:*.bak` (followed by the queried path). The same shape with --no-index printed nothing in P0-T10. diff --git a/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 b/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 new file mode 100644 index 000000000..685d79787 --- /dev/null +++ b/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 @@ -0,0 +1,9 @@ +Timestamp: 2026-10-02T06-11 +Command: git -C check-ignore -v -- foo.csproj.bak foo.rptproj.bak foo.bak +EXIT_CODE: 0 +Output Summary: Three lines in argument order, each attributed to `.gitignore:257:*.bak`. P3-T4 printed `.gitignore:259:*.bak` for the same command and P3-T10 printed nothing with `*.bak` absent; the line change confirms the removal and that `*.bak` is the covering rule. + +Output: +.gitignore:257:*.bak foo.csproj.bak +.gitignore:257:*.bak foo.rptproj.bak +.gitignore:257:*.bak foo.bak diff --git a/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 b/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 new file mode 100644 index 000000000..42684b6ad --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-11 +Command: git -C check-ignore -q -- TaskMaster.sln.bak +EXIT_CODE: 0 +Output Summary: Exit 0 with empty output on the final .gitignore: a re-created TaskMaster.sln.bak is ignored. diff --git a/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 b/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 new file mode 100644 index 000000000..1446b5604 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T06-11 +Command: git -C check-ignore -q -- README.md +EXIT_CODE: 1 +ExpectedExitCode: 1 +Output Summary: Exit 1 with empty output: README.md is not ignored, so the exit 0 of P3-T15 is discriminating. diff --git a/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 b/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 new file mode 100644 index 000000000..373d52b6f --- /dev/null +++ b/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 @@ -0,0 +1,7 @@ +Timestamp: 2026-10-02T06-11 +Command: git -C grep -n -I -F "*.bak" -- .gitignore +EXIT_CODE: 0 +Output Summary: Exactly one line, `.gitignore:257:*.bak` (P2-T11 printed line 259; the removal of two earlier lines moved it to 257). + +Output: +.gitignore:257:*.bak diff --git a/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 b/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 new file mode 100644 index 000000000..64c907db5 --- /dev/null +++ b/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 @@ -0,0 +1,21 @@ +Timestamp: 2026-10-02T06-12 +Command: git -C hash-object (six files); mcp__drm-copilot__run_poshqc_format (workspace_root=, scan_folders=["scripts/hygiene","tests/scripts/hygiene"]); same hash-object command again +EXIT_CODE: 0 +ITERATION: 1 +Output Summary: Tool returned an ok result; Hash Before and Hash After blocks are identical, so the formatter rewrote no file. + +Hash Before: +- scripts/hygiene/Test-RepositoryHygiene.ps1: e36a55d866151444849e4c67686eb211b1788989 +- scripts/hygiene/Test-RepositoryHygiene.Rules.ps1: a733b734b630e9f6184eb5818f5aeb0b337c90fa +- scripts/hygiene/Test-RepositoryHygiene.Git.ps1: 52f725ce5f3d9fc407d3d78821b7275faa4ebb93 +- tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1: c987262b34dda2cdfd77e9dfda65ea8c21caeeb2 +- tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1: db1ded33017df6e04f1b0c42e400a6a1b98c64f5 +- tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1: c767558c41232b6b157f900f5f2e36e43ce0299f + +Hash After: +- scripts/hygiene/Test-RepositoryHygiene.ps1: e36a55d866151444849e4c67686eb211b1788989 +- scripts/hygiene/Test-RepositoryHygiene.Rules.ps1: a733b734b630e9f6184eb5818f5aeb0b337c90fa +- scripts/hygiene/Test-RepositoryHygiene.Git.ps1: 52f725ce5f3d9fc407d3d78821b7275faa4ebb93 +- tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1: c987262b34dda2cdfd77e9dfda65ea8c21caeeb2 +- tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1: db1ded33017df6e04f1b0c42e400a6a1b98c64f5 +- tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1: c767558c41232b6b157f900f5f2e36e43ce0299f diff --git a/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 b/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 new file mode 100644 index 000000000..30d5ee8a5 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T06-12 +Command: mcp__drm-copilot__run_poshqc_analyze (workspace_root=, scan_folders=["scripts/hygiene","tests/scripts/hygiene"]); git -C hash-object (six files) +EXIT_CODE: 0 +ITERATION: 1 +Output Summary: PoshQC analyze: ok result; tool reports no finding count. The six hashes after analyze equal the Hash After block of the P3-T19 artifact of iteration 1 (e36a55d8..., a733b734..., 52f725ce..., c987262b..., db1ded33..., c767558c...). diff --git a/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 b/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 new file mode 100644 index 000000000..03310e516 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T06-12 +Command: mcp__drm-copilot__run_poshqc_test (workspace_root=, scan_folders=["tests/scripts/hygiene"]); Read of lines 1 to 2 of artifacts/pester/pester-junit.xml; Grep count of `status="Failed"`, of the five-name pattern with `status="Passed"`, and of the old-name pattern +EXIT_CODE: 0 +ITERATION: 1 +Output Summary: tests="49" errors="0" failures="0"; Failed count 0; five expanded-name Passed count 5 (names observed as `Invoke-RepositoryHygieneMain.reports zero findings for the backup-lookalike name `, no unexpanded template text); old-name count 0. Pester coverage of scripts/hygiene is measured by the CI Pester job; no local percentage is asserted. diff --git a/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 b/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 new file mode 100644 index 000000000..6effbbeaa --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-13 +Command: git -C add -- .gitignore tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961 +EXIT_CODE: 0 +Output Summary: Staging succeeded with explicit paths; output contained only line-ending conversion warnings (LF to CRLF) for Markdown files and no error. diff --git a/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 b/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 new file mode 100644 index 000000000..b4375b1cd --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-13 +Command: pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE' +EXIT_CODE: 0 +Output Summary: Output is exactly one line, `HYGIENE Findings=0`. diff --git a/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 b/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 new file mode 100644 index 000000000..4eaadab7c --- /dev/null +++ b/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 @@ -0,0 +1,10 @@ +Timestamp: 2026-10-02T06-14 +Command: git -C diff --name-status -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory" +EXIT_CODE: 0 +Output Summary: Exactly nine paths with the P2-T15 statuses: D for TaskMaster.sln.bak, TaskTree/TaskTree.vbproj.bak and TaskVisualization/TaskVisualization.vbproj.bak; M for .github/workflows/README.md, .gitignore, scripts/hygiene/Test-RepositoryHygiene.ps1, scripts/hygiene/Test-RepositoryHygiene.Rules.ps1, tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 and tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1. The Git adapter and its test file do not appear. Phase 3 adds no path to the Phase 2 footprint. + +Companion Command: git -C status --porcelain -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory" +Companion Output: +M .gitignore +M tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 +(only paths from the nine above; no `??` entry) diff --git a/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 b/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 new file mode 100644 index 000000000..bddd30051 --- /dev/null +++ b/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 @@ -0,0 +1,9 @@ +Timestamp: 2026-10-02T06-14 +Command: git -C diff --name-status -- docs/features +EXIT_CODE: 0 +Output Summary: Every listed path begins with docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/ except docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md, which is the single path recorded as outside the active-folder prefix by P0-T4. All entries carry status A relative to . + +Companion Command: git -C status --porcelain -- docs/features +Companion Output: Only entries beginning with docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/ (Phase 3 evidence files staged as A, the plan file, and the P3-T22 and P3-T23 artifacts pending staging); no path outside that prefix. + +Hash After (issue.md): 1afdb760bb1a20bdf67362fe40c6a9b9e49a6704 (equals the hash recorded by P3-T2; issue.md is unchanged by Phase 3). diff --git a/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 b/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 new file mode 100644 index 000000000..21f050479 --- /dev/null +++ b/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 @@ -0,0 +1,9 @@ +Timestamp: 2026-10-02T06-14 +Command: git -C merge-base --is-ancestor HEAD +EXIT_CODE: 0 +Output Summary: is an ancestor of HEAD; output empty. + +Timestamp: 2026-10-02T06-14 +Command: git -C merge-base origin/main HEAD +EXIT_CODE: 0 +Output Summary: Printed 94287369908cc920b21b0e3256314f988ad7d2f5, equal to BASE-SHA recorded in P0-T3. No BASE CHANGED condition. diff --git a/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 b/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 new file mode 100644 index 000000000..28f4f7bc3 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-09 +Command: Grep count mode on tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 for `Should`, for `-Because`, and for `Should -(BeTrue|BeFalse) -Because` +EXIT_CODE: 0 +Output Summary: `Should` 31, `-Because` 31, `Should -(BeTrue|BeFalse) -Because` 10. No -Because gap exists in the file; no edit made. diff --git a/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 b/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 new file mode 100644 index 000000000..715e17374 --- /dev/null +++ b/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 @@ -0,0 +1,6 @@ +Timestamp: 2026-10-02T05-13 +Command: mcp__drm-copilot__run_poshqc_test (workspace_root=, scan_folders=["tests/scripts/hygiene"]); Read lines 1 to 2 of artifacts/pester/pester-junit.xml; Grep `status="Failed"` (count); Grep `, scan_folders=["tests/scripts/hygiene"]); Read lines 1 to 2 of artifacts/pester/pester-junit.xml; Grep counts for status="Failed", Test-BackupFilePath passed testcases, and the clean-listing passed testcase +EXIT_CODE: 0 +Output Summary: Tool returned ok. junit root: tests="45", errors="0", failures="0". status="Failed" count = 0. Test-BackupFilePath passed-testcase count = 10. Clean-listing passed-testcase count = 1. The 12 tests that failed in P1-T4 now pass. diff --git a/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 b/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 new file mode 100644 index 000000000..a59d25db1 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T05-14 +Command: pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE' +EXIT_CODE: 1 +ExpectedExitCode: 1 +Output Summary: Exactly four lines: `HYGIENE backup-file TaskMaster.sln.bak`, `HYGIENE backup-file TaskTree/TaskTree.vbproj.bak`, `HYGIENE backup-file TaskVisualization/TaskVisualization.vbproj.bak`, `HYGIENE Findings=3`. The P0-T12 run of the same command printed `HYGIENE Findings=0`; the difference is the new rule. diff --git a/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 b/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 new file mode 100644 index 000000000..94a737bb8 --- /dev/null +++ b/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 @@ -0,0 +1,5 @@ +Timestamp: 2026-10-02T06-10 +Command: git -C check-ignore -v -- foo.bak +EXIT_CODE: 1 +ExpectedExitCode: 1 +Output Summary: Output empty and exit 1: with `*.bak` removed and neither specific line matching `foo.bak`, no rule matches. `*.bak` is the only rule covering a plain `.bak` name and the check can fail. diff --git a/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 b/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 new file mode 100644 index 000000000..1da0406ad --- /dev/null +++ b/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 @@ -0,0 +1,8 @@ +Timestamp: 2026-10-02T06-10 +Command: git -C check-ignore -v -- foo.csproj.bak foo.rptproj.bak +EXIT_CODE: 0 +Output Summary: Two lines in argument order, `.gitignore:258:*.csproj.bak` and `.gitignore:257:*.rptproj.bak`. The specific lines cover only their own names; together with P3-T10 they are not needed for anything `*.bak` covers. + +Output: +.gitignore:258:*.csproj.bak foo.csproj.bak +.gitignore:257:*.rptproj.bak foo.rptproj.bak diff --git a/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 b/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 new file mode 100644 index 000000000..1dd0c0cdc --- /dev/null +++ b/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 @@ -0,0 +1,7 @@ +Timestamp: 2026-10-02T06-10 +Command: Edit tool inserting `*.bak` after `*.csproj.bak` in .gitignore; git -C hash-object -- .gitignore; Read of .gitignore lines 255 to 260 +EXIT_CODE: 0 +Output Summary: Hash After Restore equals Hash Before (byte-identical, CRLF preserved); lines 257 to 259 read `*.rptproj.bak`, `*.csproj.bak`, `*.bak`. + +Hash Before: e0c040d97fa637a45567f032e9698074bef61b17 +Hash After Restore: e0c040d97fa637a45567f032e9698074bef61b17 diff --git a/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 b/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 new file mode 100644 index 000000000..29e1bbf6d --- /dev/null +++ b/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 @@ -0,0 +1,16 @@ +Timestamp: 2026-10-02T06-08 +Command: git -C hash-object -- tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 .gitignore docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md; Grep count mode of `^` and `\r$` on .gitignore; Grep count mode of `^` on each of the two test files +EXIT_CODE: 0 +Output Summary: Four 40-hex hashes recorded; .gitignore has 370 lines and 370 CRLF line endings (equal); Test-RepositoryHygiene.Tests.ps1 has 204 lines; Test-RepositoryHygiene.Rules.Tests.ps1 has 288 lines (both at most 500). + +Hash Before: +- tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1: bef00db2d06accf646e611816f8212ed4891ff19 +- tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1: db1ded33017df6e04f1b0c42e400a6a1b98c64f5 +- .gitignore: e0c040d97fa637a45567f032e9698074bef61b17 +- docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md: 1afdb760bb1a20bdf67362fe40c6a9b9e49a6704 + +Counts: +- .gitignore `^`: 370 +- .gitignore `\r$`: 370 +- Test-RepositoryHygiene.Tests.ps1 `^`: 204 +- Test-RepositoryHygiene.Rules.Tests.ps1 `^`: 288 diff --git a/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 b/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 new file mode 100644 index 000000000..05e872b6c --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-08 +Command: mcp__drm-copilot__run_poshqc_test (workspace_root=, scan_folders=["tests/scripts/hygiene"]); Read of lines 1 to 2 of artifacts/pester/pester-junit.xml; Grep count of ` check-ignore -v -- foo.csproj.bak foo.rptproj.bak foo.bak +EXIT_CODE: 0 +Output Summary: Three lines in argument order, each attributed to `.gitignore:259:*.bak`; the two specific lines (257, 258) are shadowed by the later `*.bak` rule, so the CR-3 redundancy premise holds. + +Output: +.gitignore:259:*.bak foo.csproj.bak +.gitignore:259:*.bak foo.rptproj.bak +.gitignore:259:*.bak foo.bak diff --git a/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 b/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 new file mode 100644 index 000000000..da63eb1a9 --- /dev/null +++ b/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 @@ -0,0 +1,4 @@ +Timestamp: 2026-10-02T06-10 +Command: Edit tool deleting the line `*.bak` from .gitignore; Read of .gitignore lines 255 to 260; Grep count of `^\*\.bak` and of `^` on .gitignore +EXIT_CODE: 0 +Output Summary: Lines 255 to 259 read `UpgradeLog*.htm`, `ServiceFabricBackup/`, `*.rptproj.bak`, `*.csproj.bak` and a blank line; Grep `^\*\.bak` count 0; Grep `^` count 369 (P3-T2 count 370 minus 1). Deliberate temporary state, reversed by P3-T12. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T05-30.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T05-30.md new file mode 100644 index 000000000..a9df295bd --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T05-30.md @@ -0,0 +1,64 @@ +# Feature Audit - Issue #961 (remaining tracked backup files and hygiene guard rule C) + +- Timestamp: 2026-10-02T05-30 +- Work Mode: minor-audit; AC source: `## Acceptance Criteria` in `issue.md` only +- Base: 94287369908cc920b21b0e3256314f988ad7d2f5; Head: f93005d38288020a2b0ebcfd2a51c1d713f7a357 + +## Scope and Baseline + +- Audit scope is the full branch diff against base 94287369908cc920b21b0e3256314f988ad7d2f5 (`git merge-base origin/main HEAD` recorded by P2-T14). +- Required footprint: deletions of `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak`, `TaskVisualization/TaskVisualization.vbproj.bak`; modifications of `.gitignore`, `.github/workflows/README.md`, `scripts/hygiene/Test-RepositoryHygiene.ps1`, `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`; feature-folder documents and the promoted record. +- Observed footprint (reviewer `git diff --name-status base HEAD`): exactly the three deletions and six modifications above, plus the feature folder, the promoted record `docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md`, and two orchestrator memory files under `.claude/agent-memory/orchestrator/` (extra, non-blocking; see code review CR-4). +- Baseline: 31 tests in `tests/scripts/hygiene`, guard output `HYGIENE Findings=0` with three tracked `.bak` files present (the rule did not exist). +- Working tree at review: `git status --short` is empty; `git ls-files -- "*.bak"` prints nothing. + +## Acceptance Criteria Inventory + +Source: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`, section `## Acceptance Criteria`. + +- AC-1: No tracked backup file remains; the three named files are deleted from the index and the worktree. +- AC-2: `.gitignore` carries a `*.bak` rule so `git check-ignore -q TaskMaster.sln.bak` exits 0. +- AC-3: The guard reports `HYGIENE backup-file ` for every tracked `.bak` path (case-insensitive), counts each as a finding, exits 1; `.claude/` stays excluded. +- AC-4: Pester tests prove the rule: negative control with finding line and exit 1, positive cases with zero findings for lookalikes and a clean listing. +- AC-5: The guard on the final tree prints `HYGIENE Findings=0` and exits 0. +- AC-6: PoshQC format, analyze and test pass; junit `errors="0" failures="0"` with a count above 31; line coverage of `scripts/hygiene` measured by CI; locally every added statement is exercised by a named test. +- AC-7: `.github/workflows/README.md` describes the backup-file rule in the `_hygiene.yml` row. + +## Acceptance Criteria Evaluation + +| AC | Verdict | Evidence verified by this review | +|---|---|---| +| AC-1 | PASS | Diff status `D` for all three files; reviewer `git ls-files -- "*.bak"` printed nothing; reviewer search for `sln.bak` and `vbproj.bak` outside `docs/`, `.claude/` and `artifacts/` found no reader. Evidence: P0-T5 (positive control printed three paths), P2-T6, P2-T7 (reads return file-not-found, glob finds none). | +| AC-2 | PASS | `.gitignore` diff adds `*.bak` at line 259 (P2-T11 exact-line grep). `git check-ignore -q TaskMaster.sln.bak` exit 0 (P2-T8); `-v` output attributes each path to `.gitignore:259:*.bak` (P2-T9); negative control `README.md` exits 1 (P2-T10). The same query with `--no-index` printed nothing at baseline (P0-T10), so the pass is attributable to the new line. | +| AC-3 | PASS | `Invoke-RepositoryHygieneMain` emits `'HYGIENE backup-file ' + $record.Path` when `Test-BackupFilePath` is true, adds it to the finding list (so `Findings=` and exit 1 follow from the existing count logic), and runs after the `.claude/` prefix skip. Case-insensitivity via `-ieq`. Real-tree control (P1-T9): three `backup-file` lines, `HYGIENE Findings=3`, exit 1. | +| AC-4 | PASS | Negative control test `reports a tracked backup file as a finding and fails the guard` asserts the exact two lines `HYGIENE backup-file TaskMaster.sln.bak` and `HYGIENE Findings=1` and `ExitCode` 1. Positive cases: lookalike test asserts `HYGIENE Findings=0`, count 0, exit 0 for five names; the existing clean-listing test covers a clean listing (P1-T8 counted its pass). Ten unit tests cover the predicate including case, depth, directory segment, `.bakery`, `.bak.md`. RED-first run (P1-T4): 12 expected failures; GREEN (P2-T3): 0 failures. | +| AC-5 | PASS | Reviewer ran the guard under `pwsh` from the worktree root: output `HYGIENE Findings=0`, exit 0. Matches P2-T17. | +| AC-6 | PASS (CI figure pending) | PoshQC format unchanged (six hashes identical, P2-T1); PoshQC analyze: pass (0 findings); tool reports no count (P2-T2); PoshQC test tests=45 (above 31), errors=0, failures=0 (P2-T3). Statement-to-test map shows 3 of 3 added statements exercised (P2-T4; re-derived from the diff). The `scripts/hygiene` LINE figure is, by the wording of this criterion, measured by CI (`_pester.yml`, LINE asserted at 80); it is not available locally (P0-T16: zero `scripts/hygiene` entries in the PoshQC document) and remains a pull-request-time gate. | +| AC-7 | PASS | README `_hygiene.yml` row (line 24) now names the backup-file rule, the final-extension `.bak` case-insensitive condition and the `HYGIENE backup-file ` finding line; one grep hit (P2-T12). Wording matches the implementation. | + +## Acceptance Criteria Check-off + +- Source file: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`. +- Newly checked off by this review: none. The executor had already checked AC-1 to AC-7; this review independently verified each against the diff and evidence and found no checkbox that the evidence contradicts. +- Unchecked: none. +- Note on AC-6: the checkbox is supported by every locally verifiable component. The CI-measured coverage component is pending and is carried as a follow-up rather than a blocker. + +## Summary + +Verdict: PASS. No blocking findings. + +### Acceptance Criteria Status +- Source: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md` +- Total AC items: 7 +- Checked off (delivered): 7 +- Remaining (unchecked): 0 +- Items remaining: none + +### Residual items and follow-ups (not filed) + +1. CI-sourced Pester LINE figure for `scripts/hygiene` (`_pester.yml`, `pester-coverage` artifact) is pending; read it after the push. A figure below the floor would turn the conditional PowerShell coverage verdict into FAIL. +2. Two orchestrator memory files under `.claude/agent-memory/orchestrator/` are in the branch diff outside the stated footprint; the orchestrator should confirm they belong in this pull request. +3. Optional test polish (low): add `-Because` to the array-equality assertions and split the five-name lookalike test into `-ForEach` cases (code review CR-1, CR-2). +4. After merge, the ruleset update that would make the hygiene check required remains an operator action tracked by #927; this change does not alter it. + +No remediation is required, so no `remediation-inputs` artifact is produced. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T06-18.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T06-18.md new file mode 100644 index 000000000..56572b7b9 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/feature-audit.2026-10-02T06-18.md @@ -0,0 +1,71 @@ +# Feature Audit - Issue #961 (re-run after remediation Phase 3) + +- Timestamp: 2026-10-02T06-18 +- Work Mode: minor-audit; AC source: `## Acceptance Criteria` in `issue.md` only +- Base: 94287369908cc920b21b0e3256314f988ad7d2f5; Head: 03f4b37c110222149e8a3f12300de22b60594d4d +- Supersedes for the final tree: `feature-audit.2026-10-02T05-30.md` (not overwritten) + +## Scope and Baseline + +- Audit scope is the full branch diff against base 94287369908cc920b21b0e3256314f988ad7d2f5 (`git merge-base origin/main HEAD`; equal to `origin/main`, so the three-dot and two-dot diffs agree). +- Required footprint: deletions of `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak`, `TaskVisualization/TaskVisualization.vbproj.bak`; modifications of `.gitignore`, `.github/workflows/README.md`, the two hygiene scripts and the two hygiene test files; feature-folder documents and the promoted record. +- Observed footprint (`git diff --numstat origin/main...HEAD`): exactly those nine paths, the feature folder, the promoted record `docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md`, and two orchestrator memory files under `.claude/agent-memory/orchestrator/` (extra, non-blocking, carried from the first cycle). Phase 3 added no path to the declared footprint (P3-T24; reviewer `git diff --stat b00fd1f19..HEAD` over `scripts tests .gitignore .github` lists only `.gitignore` and `Test-RepositoryHygiene.Tests.ps1`). +- Baseline: 31 tests in `tests/scripts/hygiene`; guard output `HYGIENE Findings=0` with three tracked `.bak` files present (the rule did not exist). +- Working tree at review: `git status --short` is empty; `git ls-files -- "*.bak"` prints nothing. + +## Acceptance Criteria Inventory + +Source: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`, section `## Acceptance Criteria` (7 items, all `- [x]`). + +- AC-1: No tracked backup file remains; three named files deleted from index and worktree. +- AC-2: `.gitignore` carries a `*.bak` rule so `git check-ignore -q TaskMaster.sln.bak` exits 0. +- AC-3: Guard reports `HYGIENE backup-file ` for every tracked `.bak` path (case-insensitive), counts each as a finding, exits 1; `.claude/` stays excluded. +- AC-4: Pester tests prove the rule: negative control and positive cases for lookalikes and a clean listing. +- AC-5: Guard on the final tree prints `HYGIENE Findings=0` and exits 0. +- AC-6: PoshQC format, analyze and test pass; junit `errors="0" failures="0"` with a count above 31; line coverage of `scripts/hygiene` measured by CI; locally every added statement exercised by a named test. +- AC-7: `.github/workflows/README.md` describes the backup-file rule in the `_hygiene.yml` row. + +## Acceptance Criteria Evaluation + +| AC | Verdict | Evidence verified by this review | +|---|---|---| +| AC-1 | PASS | Diff status `D` for all three files; reviewer `git ls-files -- "*.bak"` prints nothing; worktree clean. Prior evidence P0-T5 (positive control) and P2-T6, P2-T7 stands; Phase 3 did not touch these paths. | +| AC-2 | PASS | After CR-3, `.gitignore` line 257 is `*.bak`, the only `.bak` rule (reviewer read lines 250 to 261). Reviewer `git check-ignore -q TaskMaster.sln.bak` exits 0. P3-T14: `foo.csproj.bak`, `foo.rptproj.bak`, `foo.bak` all attributed to `.gitignore:257:*.bak`. Negative controls: P3-T16 `README.md` exits 1; P3-T9 and P3-T10 with `*.bak` temporarily removed `foo.bak` matches no rule and exits 1, so the pass is attributable to `*.bak`; P3-T12 restored the file byte-identically. The earlier P2-T9 and P2-T11 citations of `.gitignore:259` are superseded by the P3 evidence, as plan 1.4 records. | +| AC-3 | PASS | `Invoke-RepositoryHygieneMain` emits `'HYGIENE backup-file ' + $record.Path` when `Test-BackupFilePath` is true, after the `.claude/` skip; `-ieq` gives case-insensitivity; the finding is counted by the existing logic so `Findings=` and exit 1 follow. Production unchanged in Phase 3. Real-tree control (P1-T9): three lines, `HYGIENE Findings=3`, exit 1. | +| AC-4 | PASS | Negative control test asserts the exact two lines and exit 1. Positive cases: five lookalike names now run as five `-ForEach` cases, each asserting `HYGIENE Findings=0`, count 0 and exit 0; the existing clean-listing test covers a clean listing; ten predicate tests cover case, depth, directory segment, `.bakery`, `.bak.md`. RED run (P1-T4): 12 expected failures. Final: P3-T21 tests=49, failures=0. | +| AC-5 | PASS | Reviewer ran the guard under `pwsh` from the worktree root: `HYGIENE Findings=0`, exit 0. Matches P3-T23. | +| AC-6 | PASS (CI figure for the current head pending) | PoshQC format: six hashes identical (P3-T19). PoshQC analyze: ok result; tool reports no finding count (P3-T20). PoshQC test: tests=49 (above 31), errors=0, failures=0 (P3-T21). Added statements 3 of 3 exercised. CI measurement: run 36990562362 at head b00fd1f19 reported `scripts/hygiene` 98 of 104 lines (94.23 percent) and `COVERAGE LinePercent=94.64`; production scripts are byte-unchanged since that head, and the Phase 3 test edit retains every case. The CI run for head 03f4b37c1 is pending and is read by the orchestrator. | +| AC-7 | PASS | README `_hygiene.yml` row names the backup-file rule, the final-extension `.bak` case-insensitive condition and the `HYGIENE backup-file ` line; unchanged by Phase 3. | + +## Acceptance Criteria Check-off + +- Source file: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`. +- Newly checked off by this review: none. The 7 items were already checked; each was independently verified above and no checkbox is contradicted by the evidence. +- Unchecked: none. +- `issue.md` was not edited by this review. + +## Remediation Verification Summary + +| Item | Status | Basis | +|---|---|---| +| CR-1 lookalike test split into `-ForEach`, five cases, each with `-Because` | Closed | Tests.ps1 lines 171 to 186; P3-T21 five expanded passed names | +| CR-2 `-Because` on array-equality assertions and related siblings | Closed | 29 of 29 and 31 of 31 assertions carry `-Because` | +| CR-3 `.gitignore` reduced to `*.bak`; AC-2 intact; negative-control evidence | Closed | `.gitignore` line 257; P3-T9 to P3-T16 | + +## Summary + +Verdict: PASS. No blocking findings. Remaining findings: CR-4 Info, RELATED (two orchestrator memory files outside the declared footprint, orchestrator to confirm); CR-6 Info, UNRELATED (7 assertions without `-Because` in `Test-RepositoryHygiene.Git.Tests.ps1`, a file this branch does not touch). + +### Acceptance Criteria Status +- Source: `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md` +- Total AC items: 7 +- Checked off (delivered): 7 +- Remaining (unchecked): 0 +- Items remaining: none + +### Pending at pull-request time + +1. CI Pester LINE figure for `scripts/hygiene` at head 03f4b37c1 (`_pester.yml`, `pester-coverage` artifact) is pending; the orchestrator reads the CI run. A changed file below the 85 percent line floor would turn the conditional PowerShell coverage verdict into FAIL. Expected outcome is unchanged from 94.23 percent because no production line changed. +2. The ruleset update that would make the hygiene check required remains an operator action tracked by #927. + +No remediation is required, so no `remediation-inputs` artifact is produced. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md new file mode 100644 index 000000000..1afdb760b --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md @@ -0,0 +1,88 @@ +# remaining-tracked-backup-files-and-hygiene-guard-rule (Issue #961) + +- Date captured: 2026-10-01 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/remaining-tracked-backup-files-and-hygiene-guard-rule/ (Issue #961) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #961 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/961 +- Last Updated: 2026-10-01 +- Work Mode: minor-audit + +## Summary + +#951 (PR #960) removed the eight tracked `*.csproj.bak` files and added an ignore rule. Two gaps remain: +- three other tracked backup files on `main`; +- no CI rule preventing a forced add from reintroducing them. + +An ignore rule does not stop `git add -f`. + +## Environment + +- OS/version: n/a +- Python version: n/a +- Command/flags used: `git ls-tree -r --name-only origin/main | grep -E "\.bak$"` +- Data source or fixture: repository tree at `9a3d2dd3e` or later + +## Steps to Reproduce + +1. List tracked `.bak` files on `main`: + - `TaskMaster.sln.bak` + - `TaskTree/TaskTree.vbproj.bak` + - `TaskVisualization/TaskVisualization.vbproj.bak` +2. Force-add any `*.csproj.bak`. The hygiene guard reports nothing. + +## Expected Behavior + +- No tracked backup files. +- The repository hygiene guard (#927, `scripts/hygiene/`) fails CI when any tracked `*.bak` exists. + +## Actual Behavior + +Three backups remain, and the guard has no backup-file rule. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: #951 executor follow-ups 1 and 2 (PR #960). + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [ ] Medium +- [x] Low + +## Suspected Cause / Notes + +#951 was scoped to `*.csproj.bak` only. Confirm that no build step reads the `.sln.bak` or `.vbproj.bak` files before deleting them. + +Pre-plan findings recorded by the preparation run (verified against the worktree at origin/main 860d67bf4): + +- Exactly three tracked backup files exist and no other backup-family file (`.orig`, `.rej`, `.old`, `.tmp`, `.swp`) is present on disk or tracked. The #951 audit and a repository-wide glob agree. +- No build step, project file, workflow or script reads `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak` or `TaskVisualization/TaskVisualization.vbproj.bak`. Matches outside `docs/` and `.claude/` for the `.bak` token are limited to `.gitignore` and one test string literal in `UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs`. +- `.gitignore` already carries `*.rptproj.bak` and `*.csproj.bak` (lines 257 and 258) and does not cover `*.sln.bak` or `*.vbproj.bak`. The plan adds a general `*.bak` rule. +- Rule scope decision: the guard matches the `.bak` extension only, case-insensitively, on the final path segment. A broader family is not adopted because the issue scopes `*.bak`, the tree holds no other backup-family file to justify it, and `.old`, `.tmp` and `.orig` are legitimate fixture or tool-output suffixes in some trees, which would create false positives. +- The guard keeps its single governance-directory exclusion (`.claude/`); the backup rule applies to every other tracked path and adds no exemption mechanism. + +## Acceptance Criteria + +- [x] AC-1: No tracked backup file remains. `git ls-files -- "*.bak"` prints nothing, and the three files `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak` and `TaskVisualization/TaskVisualization.vbproj.bak` are deleted from the index and the worktree. +- [x] AC-2: `.gitignore` carries a `*.bak` rule, so `git check-ignore -q TaskMaster.sln.bak` exits 0 for a re-created file of that name. +- [x] AC-3: The repository hygiene guard reports `HYGIENE backup-file ` for every tracked path whose extension is `.bak` (case-insensitive), counts each as a finding, and exits 1; paths under the `.claude/` governance directory stay excluded as for the existing rules. +- [x] AC-4: Pester tests under `tests/scripts/hygiene/` prove the rule: a negative control seeds a tracked `.bak` path and asserts the finding line and exit code 1, and positive cases assert zero findings for `.bak`-lookalike names and for a clean listing. +- [x] AC-5: The hygiene guard run against the final tree prints `HYGIENE Findings=0` and exits 0. +- [x] AC-6: PoshQC format, analyze and test pass for the changed PowerShell files. The PoshQC test run over `tests/scripts/hygiene` reports `errors="0" failures="0"` in `artifacts/pester/pester-junit.xml` with a test count above the 31-test baseline. Line coverage of `scripts/hygiene` is measured by CI (`_pester.yml`), because the PoshQC coverage document does not include `scripts/hygiene` in its denominator; locally, every added statement is exercised by a named test. +- [x] AC-7: `.github/workflows/README.md` describes the backup-file rule in the `_hygiene.yml` row. + +## Proposed Fix / Validation Ideas + +- [ ] Delete the three files, and broaden the ignore rule to `*.bak` if appropriate. +- [ ] Add a guard rule that flags tracked `*.bak` paths, with Pester tests and a negative control proving the rule fails on a seeded path. Run it through PoshQC, with coverage from CI. + +## 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-remaining-tracked-backup-files-and-hygiene-guard-rule-961/plan.2026-10-02T02-25.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/plan.2026-10-02T02-25.md new file mode 100644 index 000000000..da73d71cf --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/plan.2026-10-02T02-25.md @@ -0,0 +1,163 @@ +# remaining-tracked-backup-files-and-hygiene-guard-rule (Plan) + +- **Issue:** #961 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Last Updated:** 2026-10-02T02-25 +- **Status:** Draft +- **Version:** 1.4 +- **Work Mode:** minor-audit + +DIRECTIVE: MINIMAL-AUDIT PLAN REQUIRED + +**Requirements source:** `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`, section `## Acceptance Criteria` (AC-1 to AC-7). No `spec.md`, `user-story.md` or `research.md` is required. + +**Fail-closed evidence rule:** If any required baseline artifact, final-QC artifact or coverage-limitation artifact is missing or lacks a required field, the audit verdict must be BLOCKED or INCOMPLETE, never PASS. + +**Evidence accounting rule:** Each command-bearing task names its evidence artifact. Do not check a task off without its artifact. Every command-step artifact records `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`. An artifact whose command is expected to exit non-zero also records `ExpectedExitCode: 1` on its own line. + +## Revision Log + +- **1.4 (2026-10-02):** Eight preflight deltas applied to Phase 3 in place; no acceptance criterion in `issue.md` is edited and no checked task is re-opened. (1) P3-T5 asserts two placeholder-free tokens. (2) P3-T5 fixes the hashtable layout to one hashtable per line. (3) P3-T3 drops the trailing space from its post-fix pattern. (4) The Rules test file is read-only in Phase 3 (preamble and P3-T1); P3-T8 stays a read-only sweep. (5) P3-T20 records one exact phrase. (6) P3-T6 locates assertion (b) by its enclosing test title. (7) This entry and P3-T18 name the historical records below. (8) P3-T21 asserts `tests="49"` exactly. Historical records of the Phase 2 tree that are not re-run and not edited: the P2-T4 expectation of 4 `Invoke-RepositoryHygieneMain.` names (it reads 3 once the P1-T3 lookalike case is renamed and split by P3-T5, because the old name alternation no longer matches the five expanded names); the `.gitignore:259` citations in P2-T9 and P2-T11 and in their evidence; the `.gitignore:258` citation in the P0-T11 evidence; and the "consecutive lines" statement in P1-T11 and its evidence. Phase 3 artifacts supersede them for the final tree. +- **1.3 (2026-10-02):** Widened in place with Phase 3 (Review Remediation, CR-1 to CR-3) after the code review of the Phase 0-2 result. No acceptance criterion in `issue.md` is edited and no checked Phase 0-2 task is re-opened. Phase 3 supersedes three statements of Phases 0-2 for the tree it leaves behind, each annotated at its location: the design decision that leaves `*.rptproj.bak` and `*.csproj.bak` untouched (CR-3 removes both), the statement that no `-ForEach` is used (CR-1 uses one), and the 45-test post-change count (CR-1 raises it to 49). The header `Last Updated` value is unchanged because the clock is not readable by the planner; the executor records clock timestamps in every Phase 3 artifact. +- **1.2 and earlier:** Phases 0-2 as authored and executed. + +## Scope + +Declared footprint, every file this plan writes (exact paths): + +- delete `TaskMaster.sln.bak` +- delete `TaskTree/TaskTree.vbproj.bak` +- delete `TaskVisualization/TaskVisualization.vbproj.bak` +- edit `.gitignore` +- edit `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1` +- edit `scripts/hygiene/Test-RepositoryHygiene.ps1` +- edit `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` +- edit `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` +- edit `.github/workflows/README.md` +- edit `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md` (AC check-off only) +- write the plan file and evidence files under `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/` + +Everything else is out of scope, including the promoted potential record, the Git adapter `scripts/hygiene/Test-RepositoryHygiene.Git.ps1` (stays unchanged), its test file, any C# file and any workflow YAML. + +The promoted record `docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md` is carried by the preparation step, is not written by this plan, and is accounted for in P0-T4 and P2-T16. + +Design decisions (made before planning, justified in `issue.md` Suspected Cause / Notes): + +- The guard gains rule C: a tracked path whose final extension is `.bak`, matched case-insensitively with `[System.IO.Path]::GetExtension`, after the existing `.claude/` governance skip, with no new exemption mechanism. A broader backup-file family is not adopted. +- The finding line is `HYGIENE backup-file `. A backup file is still content-scanned (no early `continue`), so a `.bak` file that also carries a profile path yields both findings. +- The pure rule is a new function `Test-BackupFilePath` in `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`. The wiring is one `if` in `Invoke-RepositoryHygieneMain` in `scripts/hygiene/Test-RepositoryHygiene.ps1`. This stays within the budget of 2 production PowerShell files. +- `.gitignore` gains a general `*.bak` line directly after the existing `*.csproj.bak` line; the two existing lines (`*.rptproj.bak`, `*.csproj.bak`) are untouched in Phases 0-2 (amended by Phase 3, task CR-3: both are then removed as redundant with `*.bak`). +- Coverage limitation: the PoshQC coverage document `artifacts/pester/powershell-coverage.xml` does not carry `scripts/hygiene` in its denominator, so no hygiene line-coverage percentage can be read locally and none is asserted. CI (`.github/workflows/_pester.yml`, which asserts `LINE` at 80 over `scripts/hygiene`) measures it. Locally, each added statement is mapped to a named passing test (P2-T4). The limitation is recorded by P0-T16. + +## Execution rules for every task + +- **Bash discipline (binding, pass verbatim to the executor).** Never use `cd`. Address the item worktree with `git -C ...` for every git command, and pass absolute paths to every other tool. Never invoke grep, sed, awk, cat, head, tail, find, cp, mv, rm or echo through Bash; use the Grep, Read, Glob, Edit and Write tools with absolute paths instead. The only Bash forms permitted are single commands (no `&&`, `;` or `|` chaining) whose first token is `git`, `pwsh` or `poetry`, plus the three `.claude/lib/bash/*.sh` scripts. If a shell step is genuinely needed, run it as one `pwsh -NoProfile -Command '...'` invocation with the worktree path inside the command string, never as a `cd`. If the shell refuses a `pwsh` or `git` form, stop and report BLOCKED; do not substitute another tool. Tasks P0-T12, P1-T9 and P2-T17 run `pwsh` through the shell. A worktree-isolated executor's shell refuses every `pwsh` form, so these three tasks require a non-isolated executor; when the shell refuses the form, stop at that task and report BLOCKED. +- `` is a placeholder for the absolute path of the item worktree. Substitute it at run time in every tool call. In every evidence file and in the `Command:` field write the placeholder ``, never the literal absolute path. The repository hygiene guard flags a tracked file outside `.claude/` that contains a drive-letter user-profile path, so no evidence file may carry one. Before finishing each task, read the evidence file back and redact any absolute host path, drive letter path, machine name or account name. Raw tool documents (the junit and coverage XML) are never copied or committed; evidence records only the counts and the repository-relative test names stated in the task. +- `` in an artifact file name is the write time formatted `yyyy-MM-ddTHH-mm`, equal to the artifact's own `Timestamp:` value. The only fixed artifact name is `phase0-instructions-read.md`. Every artifact path below is written in full under `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence//`; the only kinds used are `baseline`, `qa-gates`, `regression-testing` and `other`. +- Anchor every diff to the commit `BASE-SHA`, the value printed by `git merge-base origin/main HEAD` and recorded by P0-T3. Substitute the recorded 40-hex-digit value wherever a command below shows ``, and write `` in evidence files. The plan never diffs against the ref `origin/main` itself: that ref is shared by every worktree and advances when a sibling item merges, whereas the merge-base stays at this branch's fork point until HEAD is rewritten. The plan never runs `git fetch` and never merges or rebases. +- PoshQC tools: `mcp__drm-copilot__run_poshqc_format`, `mcp__drm-copilot__run_poshqc_analyze` and `mcp__drm-copilot__run_poshqc_test` are called with `workspace_root` set to the substituted worktree root. Format and analyze use `scan_folders` equal to `["scripts/hygiene", "tests/scripts/hygiene"]`; test uses `scan_folders` equal to `["tests/scripts/hygiene"]`. The tools return a short summary and no numbers, so each task states its own observation. For a PoshQC call, `EXIT_CODE` records 0 when the tool returned an ok result and 1 otherwise. The junit document `artifacts/pester/pester-junit.xml` is read with the Read tool (lines 1 to 2) and the Grep tool with that explicit file path (ripgrep skips the git-ignored `artifacts/` tree unless the file is named explicitly). In that document the root element is `testsuites`, each test is one `.md` with the four required fields (`EXIT_CODE: 0`; `Command:` lists the tool calls). Acceptance: the artifact records 1 section hit, 7 unchecked AC lines and zero spec, user-story or research files; otherwise stop and report. +- [x] [P0-T3] Record the base commit (no fetch): run `git -C merge-base origin/main HEAD` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t3-base-sha..md` with the four required fields; the `Output Summary:` names the printed 40-character value as `BASE-SHA`. Acceptance: EXIT_CODE 0 and exactly one 40-hex-digit output line. A non-zero exit means `origin/main` and `HEAD` share no history: stop and report. Later diff tasks compare against this value, so a newer position of `origin/main` does not affect them. +- [x] [P0-T4] Carried-documents baseline: run `git -C diff --name-status -- docs/features` and, as the status companion, `git -C status --porcelain -- docs/features`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t4-carried-docs..md` with the four required fields for the diff plus a `Companion Command:` and `Companion Output:` pair. Acceptance: the diff EXIT_CODE is 0 and every listed path and companion entry either begins with `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/` or equals `docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md` (the promotion record carried by the preparation step). The `Output Summary:` lists the paths that do not begin with the active-folder prefix. P2-T16 compares against that list. +- [x] [P0-T5] AC-1 baseline (fail-before): run `git -C ls-files -- "*.bak"` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t5-ac1-baseline..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output lists exactly three paths: `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak` and `TaskVisualization/TaskVisualization.vbproj.bak`. +- [x] [P0-T6] Case-insensitive inventory: run `git -C ls-files -- ":(icase)*.bak"` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t6-icase-inventory..md` with the four required fields. The rule being added is case-insensitive, while the pathspec of P0-T5 is case-sensitive; this command shows no upper-case variant is tracked. Acceptance: EXIT_CODE 0 and the output lists exactly the same three paths as P0-T5. +- [x] [P0-T7] Working-tree inventory: with the Glob tool, glob `**/*.bak` from the worktree root and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t7-worktree-inventory..md` with `Timestamp:`, `Command:` (the Glob call), `EXIT_CODE: 0` and `Output Summary:` listing the repository-relative matches. Acceptance: exactly the three paths of P0-T5 are returned and no other file; this is the positive control for the P2-T7 absence check. +- [x] [P0-T8] No-reader search: run `git -C grep -l -I -F ".bak" -- . ":(exclude)docs" ":(exclude).claude"` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t8-no-reader-search..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output is exactly two file names, `.gitignore` and `UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs`. No project, solution, script or workflow file reads the three backups. +- [x] [P0-T9] No-reader search control: run `git -C grep -l -I -F ".bak" -- .gitignore` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t9-search-control..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output is the single line `.gitignore`, proving the P0-T8 search shape can match. +- [x] [P0-T10] AC-2 baseline (fail-before): run `git -C check-ignore -v --no-index -- TaskMaster.sln.bak TaskTree/TaskTree.vbproj.bak TaskVisualization/TaskVisualization.vbproj.bak` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t10-ac2-baseline..md` with the four required fields plus the line `ExpectedExitCode: 1`. The `--no-index` flag is required because `git check-ignore` skips tracked paths without it. Acceptance: EXIT_CODE 1 and the output is empty (no ignore rule covers the three names before the change). +- [x] [P0-T11] Check-ignore shape control: run `git -C check-ignore -v --no-index -- QuickFiler/QuickFiler.csproj.bak` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t11-check-ignore-control..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output begins `.gitignore:258:*.csproj.bak`, proving the P0-T10 command shape reports an ignore rule when one exists. The path need not exist on disk. +- [x] [P0-T12] Guard baseline over the tree (the guard has no backup rule yet): run `pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE'` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t12-guard-baseline..md` with the four required fields. The `Set-Location` is inside the command because `pwsh -File` resolves a relative script path against the caller's directory, not against a working-directory option. Acceptance: EXIT_CODE 0 and the output is exactly the one line `HYGIENE Findings=0`. This is the AC-3 fail-before record: the three tracked backups from P0-T5 are present and the guard reports nothing. +- [x] [P0-T13] Format baseline (write-mode tool): run `git -C hash-object -- scripts/hygiene/Test-RepositoryHygiene.ps1 scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 scripts/hygiene/Test-RepositoryHygiene.Git.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1`, then invoke `mcp__drm-copilot__run_poshqc_format` as stated in the execution rules, then run the same hash command again. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t13-format-baseline..md` with the four required fields plus `Hash Before:` and `Hash After:` blocks (six lines each). A formatter exits ok whether or not it rewrote a file, so the observation is the before-and-after hash comparison, not the tool status. Acceptance: the tool returned an ok result (EXIT_CODE 0) and the two hash blocks are identical, meaning the six files were already formatted. If they differ, stop and report: the formatter rewrote a file outside the declared footprint. +- [x] [P0-T14] Analyze baseline: invoke `mcp__drm-copilot__run_poshqc_analyze` as stated in the execution rules, then run `git -C status --porcelain -- scripts/hygiene tests/scripts/hygiene`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t14-analyze-baseline..md` with the four required fields; `Output Summary:` quotes the tool's returned summary sentence verbatim (the tool returns an ok flag and one sentence, not a diagnostic count) and the porcelain output. Acceptance: the tool returned an ok result (EXIT_CODE 0) and the porcelain output is empty (analyze is read-only). If the tool reports findings, stop and report them as pre-existing. +- [x] [P0-T15] Test baseline: invoke `mcp__drm-copilot__run_poshqc_test` as stated in the execution rules, then Read lines 1 to 2 of `artifacts/pester/pester-junit.xml`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/baseline/p0-t15-test-baseline..md` with the four required fields; `Output Summary:` records only the root-element values `tests`, `errors` and `failures` (not the raw line, which carries absolute paths). Acceptance: `tests="31"`, `errors="0"` and `failures="0"`. A different `tests` value means the tree differs from the planned baseline: stop and report. +- [x] [P0-T16] Coverage limitation record: with the Grep tool and the explicit file path `artifacts/pester/powershell-coverage.xml` (regenerated by P0-T15), count matches of `scripts/hygiene` (expected 0) and count matches of `.md` with `Timestamp:`, `Command:` (the Grep calls), `EXIT_CODE: 0` and `Output Summary:` stating the two counts, the informational LINE counter values (missed and covered), and the sentence that coverage of `scripts/hygiene` is measured by CI in `_pester.yml` (LINE at 80) and not by this document. Acceptance: the `scripts/hygiene` count is 0, the `.md` containing `Timestamp:`, the declared footprint from the Scope section verbatim, the binding Bash discipline from the execution rules, and the sentence that the executor edits only the footprint paths. Acceptance: the artifact exists with those elements. No file other than the footprint may be touched in Phase 1. +- [x] [P1-T2] Add the rule tests: with the Edit tool, append to `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` (after the closing brace of the last `Describe`, whose last assertion carries the `-Because` text `the extension gate keeps non-XML files out of rule A`) a new `Describe 'Test-BackupFilePath'` block. Its `BeforeAll` dot-sources `../../../scripts/hygiene/Test-RepositoryHygiene.ps1` exactly as the two sibling blocks do. It holds 10 `It` blocks, one behavior each, every assertion carrying `-Because`, no temp file, no real git: `returns true for a root-level solution backup` (`TaskMaster.sln.bak`), `returns true for a nested project backup` (`TaskTree/TaskTree.vbproj.bak`), `returns true for an upper-case extension` (`Notes.BAK`), `returns true for a bare dot-bak file name` (`.bak`), `returns false for a directory named bak` (`docs/bak/notes.md`), `returns false for a longer extension that begins with bak` (`notes.bakery`), `returns false when bak is not the final extension` (`notes.bak.md`), `returns false for the word backup` (`backup`), `returns false for a path with no extension` (`docs/features/x/Makefile`), `returns false for the bare word bak` (`bak`). Each calls `Test-BackupFilePath -RelativePath ` and asserts `Should -BeTrue` or `Should -BeFalse`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t2-rule-tests-added..md` with the four required fields. Acceptance: Grep `^Describe 'Test-BackupFilePath' \{` in that file returns 1 hit; Grep `^\s*It ` in that file returns 29 hits (19 + 10); Grep `^` in count mode returns at most 500 lines. +- [x] [P1-T3] Add the orchestration tests: with the Edit tool, add 4 `It` blocks inside the existing `Describe 'Invoke-RepositoryHygieneMain'` of `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1`, after the last existing `It`. They follow the file's style: path list built with `ConvertTo-EolListing`, content injected through `$script:Reader` and `$script:Content`, assertions with `-Because`. The profile-path fixture is the existing run-time-assembled `$script:Violation`; no contiguous drive-rooted path literal is written. The blocks are: (a) `reports a tracked backup file as a finding and fails the guard`, the negative control: listing `TaskMaster.sln.bak` with plain-text content asserts `Lines` equals `@('HYGIENE backup-file TaskMaster.sln.bak', 'HYGIENE Findings=1')` and `ExitCode` equals 1; (b) `reports zero findings for backup-lookalike names`: listing `docs/bak/notes.md`, `notes.bakery`, `notes.bak.md`, `backup` and `docs/features/x/Makefile`, each with plain-text content, asserts `Lines` equals `@('HYGIENE Findings=0')`, `FindingCount` 0 and `ExitCode` 0; (c) `drops a governance-directory backup record before the backup rule runs`: listing `.claude/agent-memory/notes.bak` asserts `Lines` equals `@('HYGIENE Findings=0')` and `ExitCode` 0; (d) `still scans a backup file for a profile path`: listing `docs/features/x/old.bak` with content `$script:Violation` asserts `Lines` equals `@('HYGIENE backup-file docs/features/x/old.bak', 'HYGIENE profile-path docs/features/x/old.bak:1', 'HYGIENE Findings=2')` and `ExitCode` 1. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t3-orchestration-tests-added..md` with the four required fields. Acceptance: Grep `^\s*It ` in that file returns 11 hits (7 + 4); Grep for the alternation of the four exact `It` names above returns 4 hits; Grep `^` in count mode returns at most 500 lines. +- [x] [P1-T4] [expect-fail] Run the new tests before any production change: invoke `mcp__drm-copilot__run_poshqc_test` as stated in the execution rules, then Read lines 1 to 2 of `artifacts/pester/pester-junit.xml` and Grep that file for `status="Failed"` (count) and for `.md` with the four required fields plus `Expect-Fail: true`; `Output Summary:` records only the counts. If the tool returned an ok result, record `EXIT_CODE: 0`. If it returned a not-ok result because tests failed, record `EXIT_CODE: 1` and add the line `ExpectedExitCode: 1` on its own line. Either outcome is accepted; the acceptance below is decided by the junit counts alone. Acceptance: root `tests="45"`, root `failures="12"` (10 rule tests, plus the negative control (a) and the combined case (d) of P1-T3; the lookalike case (b) and the governance case (c) pass before the fix because the guard already reports nothing for them), the `Test-BackupFilePath` failed-testcase count is 10, and the `status="Failed"` count is 12. Record `errors` without asserting it. Any other count means stop and report. +- [x] [P1-T5] Add the pure rule: with the Edit tool, append to `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1` (after `Get-RawEvidenceDocumentKind`, the last function) a function `Test-BackupFilePath` with `[CmdletBinding()]`, `[OutputType([bool])]`, a mandatory `[string]$RelativePath` parameter, comment-based help (`.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.OUTPUTS`) in the style of its siblings, and a body that returns the result of `[System.IO.Path]::GetExtension($RelativePath) -ieq '.bak'`, with `.bak` in single quotes as in the sibling expression `-ieq '.xml'` of `Invoke-RepositoryHygieneMain`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t5-rule-function-added..md` with the four required fields. Acceptance: Grep `^function Test-BackupFilePath` in that file returns 1 hit; Grep `ieq '\.bak'` in that file returns 1 hit; Grep `^` in count mode returns at most 500 lines. +- [x] [P1-T6] Wire the rule: with the Edit tool, in `Invoke-RepositoryHygieneMain` of `scripts/hygiene/Test-RepositoryHygiene.ps1`, insert immediately after the governance `continue` block and before the line `$text = $null` the statement `if (Test-BackupFilePath -RelativePath $record.Path) { $lines.Add('HYGIENE backup-file ' + $record.Path) }`, formatted over three lines in the file's brace style, with no `continue`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t6-guard-rule-wired..md` with the four required fields. Acceptance: Grep `Test-BackupFilePath -RelativePath` in that file returns 1 hit; Grep `HYGIENE backup-file` in that file returns 1 hit; a Read of the function shows the new `if` after the governance `continue` block and before `$text = $null`, and no `continue` inside the new block. +- [x] [P1-T7] Update the guard documentation: with the Edit tool, in `scripts/hygiene/Test-RepositoryHygiene.ps1` (a) extend the header comment so rule A and rule B are followed by a backup-file rule written `(rule C)`, (b) change the `.SYNOPSIS` text that names both hygiene rules so it names the rules without the word `both`, and (c) extend `.DESCRIPTION` to state that a record whose final extension is `.bak` yields `HYGIENE backup-file ` and is still content-scanned. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t7-guard-docs-updated..md` with the four required fields. Acceptance: Grep `\(rule C\)` in that file returns 1 hit; Grep `HYGIENE backup-file` in that file returns 2 hits (code from P1-T6 plus the description); Grep `both hygiene rules` in that file returns 0 hits (the phrase was present before the edit); Grep `^` in count mode returns at most 500 lines. +- [x] [P1-T8] Run the tests after the fix: invoke `mcp__drm-copilot__run_poshqc_test` as stated in the execution rules, then Read lines 1 to 2 of `artifacts/pester/pester-junit.xml`, Grep that file for `status="Failed"` (count), for `.md` with the four required fields; `Output Summary:` records only the counts. Acceptance: root `tests="45"`, `errors="0"` and `failures="0"`, the `status="Failed"` count is 0, the `Test-BackupFilePath` passed-testcase count is 10 and the clean-listing passed-testcase count is 1. The same 12 tests that failed in P1-T4 now pass. +- [x] [P1-T9] [expect-fail] Guard negative control over the real tree, with the backups still tracked: run `pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE'` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/regression-testing/p1-t9-guard-negative-control..md` with the four required fields plus the line `ExpectedExitCode: 1`. Acceptance: EXIT_CODE 1 and the output is exactly four lines in this order: `HYGIENE backup-file TaskMaster.sln.bak`, `HYGIENE backup-file TaskTree/TaskTree.vbproj.bak`, `HYGIENE backup-file TaskVisualization/TaskVisualization.vbproj.bak`, `HYGIENE Findings=3`. The P0-T12 run of the same command printed `HYGIENE Findings=0`; the difference is the new rule. +- [x] [P1-T10] Remove the three tracked backups with one command: `git -C rm -- TaskMaster.sln.bak TaskTree/TaskTree.vbproj.bak TaskVisualization/TaskVisualization.vbproj.bak`, and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t10-git-rm..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output is three lines of the form `rm ''`, one per path. Remove no other file. +- [x] [P1-T11] Edit `.gitignore` with the Edit tool: insert the exact new line `*.bak` immediately after the existing line `*.csproj.bak` (line 258 before the edit, inside the block that begins with the comment about backup files from converting an old project file). Keep `*.rptproj.bak` and `*.csproj.bak` unchanged and add no `!` negation line. The file uses CRLF line endings (369 lines, all ending in CR, before the edit). Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t11-gitignore-edit..md` with `Timestamp:`, `Command:` (the Edit call and the verification calls), `EXIT_CODE: 0` and `Output Summary:`. Acceptance: a Read of `.gitignore` lines 255 to 261 shows `*.rptproj.bak`, `*.csproj.bak` and `*.bak` on consecutive lines; Grep `^` in count mode returns 370; Grep `\r$` in count mode returns 370, or 369 when the inserted line carries LF only, which is recorded in the artifact and accepted because `.gitattributes` sets `* text=auto`. Use no other tool to rewrite line endings. +- [x] [P1-T12] Edit the `_hygiene.yml` table row (line 24) of `.github/workflows/README.md` with the Edit tool. The row currently says the guard fails on a raw test-platform or coverage-collector document (classified by content) or on a Windows user-profile path pattern in any tracked file outside .claude, and prints path and line only. Replace that clause so the row reads: fails on a raw test-platform or coverage-collector document (classified by content), on a tracked backup file whose final extension is .bak (case-insensitive, finding line `HYGIENE backup-file `) or on a Windows user-profile path pattern in any tracked file outside .claude; prints the rule name, the path and, for a profile path, a line number only. Change nothing else in the file. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p1-t12-readme-row-edit..md` with the four required fields. Acceptance: Grep `_hygiene.yml.*HYGIENE backup-file` in that file returns 1 hit; Grep `fails on a raw test-platform` returns 1 hit; the counts of Grep `^` and Grep `\r$` in count mode are equal (the file's CRLF endings are preserved). + +### Phase 2 — Final QC + +Every task below is unconditional and has no skip path. Tasks P2-T1 to P2-T3 form the toolchain loop in the order format, analyze, test (P2-T4 and P2-T5 follow the loop). If any of them changes a file or fails, fix the cause inside the declared footprint and restart at P2-T1; each restart writes new artifacts whose `` differs and whose `ITERATION:` field increments from 1. The artifact with the highest `ITERATION:` per task is the one later tasks use. The loop is complete only when one pass runs P2-T1 to P2-T3 with no change and no failure. + +- [x] [P2-T1] Format (write-mode tool): run `git -C hash-object -- scripts/hygiene/Test-RepositoryHygiene.ps1 scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 scripts/hygiene/Test-RepositoryHygiene.Git.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1`, invoke `mcp__drm-copilot__run_poshqc_format` as stated in the execution rules, then run the same hash command again. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t1-format..md` with the four required fields, `ITERATION: `, and `Hash Before:` and `Hash After:` blocks. The tool exits ok whether or not it rewrote a file, so the observation is the hash comparison. Acceptance: the tool returned an ok result (EXIT_CODE 0) and the two hash blocks are identical. Identical blocks mean the formatter changed nothing; differing blocks mean the loop restarts at P2-T1 after the artifact is written. +- [x] [P2-T2] Analyze: invoke `mcp__drm-copilot__run_poshqc_analyze` as stated in the execution rules, then run `git -C hash-object -- scripts/hygiene/Test-RepositoryHygiene.ps1 scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 scripts/hygiene/Test-RepositoryHygiene.Git.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t2-analyze..md` with the four required fields, `ITERATION: `, and the tool's returned summary sentence quoted verbatim. Acceptance: the tool returned an ok result (EXIT_CODE 0) with no reported finding and the six hashes equal the `Hash After:` block of the P2-T1 artifact of the same iteration (analyze changed nothing). +- [x] [P2-T3] Test: invoke `mcp__drm-copilot__run_poshqc_test` as stated in the execution rules, then Read lines 1 to 2 of `artifacts/pester/pester-junit.xml` and Grep that file for `status="Failed"` (count). Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t3-test..md` with the four required fields, `ITERATION: `; `Output Summary:` records only `tests`, `errors`, `failures` and the Failed count. Acceptance: `tests="45"` (above the 31-test baseline of P0-T15), `errors="0"`, `failures="0"` and a `status="Failed"` count of 0. +- [x] [P2-T4] Statement-to-test coverage map (local substitute for the hygiene coverage figure that only CI measures, see P0-T16). With the Grep tool and the explicit file path `artifacts/pester/pester-junit.xml`, count matches of `.md` with `Timestamp:`, `Command:` (the Grep calls), `EXIT_CODE: 0` and `Output Summary:` carrying this map: the `Test-BackupFilePath` body is exercised by its 10 passing tests (true branch of the comparison by the 4 positive tests, false branch by the 6 negative tests); the new `if` in `Invoke-RepositoryHygieneMain` is exercised on its true side by the negative control (a) and the combined case (d), and on its false side by the lookalike case (b); the governance skip ahead of it by case (c). Acceptance: the two counts are 10 and 4, and the artifact carries the map and the sentence that CI measures `scripts/hygiene` line coverage. +- [x] [P2-T5] File-size check: with the Grep tool in count mode, count `^` in each of `scripts/hygiene/Test-RepositoryHygiene.ps1`, `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` and `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t5-file-sizes..md` with `Timestamp:`, `Command:`, `EXIT_CODE: 0` and `Output Summary:` listing the four counts. Acceptance: every count is at most 500 (the policy file-size limit); the baseline counts were 87, 123, 159 and 222. +- [x] [P2-T6] AC-1 index check: run `git -C ls-files -- "*.bak"` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t6-ac1-index..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output is empty (the same command printed three paths in P0-T5, its positive control). +- [x] [P2-T7] AC-1 working-tree check: with the Read tool read `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak` and `TaskVisualization/TaskVisualization.vbproj.bak`, with the Glob tool glob `**/*.bak`, and with the Read tool read `.gitignore` (limit 1) as a control. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t7-ac1-worktree-absence..md` with `Timestamp:`, `Command:` (the five tool calls), `EXIT_CODE: 0` and `Output Summary:`. Acceptance: each of the three backup reads returns a file-not-found error, the glob returns no file (it returned three in P0-T7), and the control read succeeds. +- [x] [P2-T8] AC-2 exact check: run `git -C check-ignore -q TaskMaster.sln.bak` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t8-ac2-check-ignore-q..md` with the four required fields. The path no longer exists and is no longer tracked after P1-T10, so the command evaluates it as a re-created file; the quiet form prints nothing on success. Acceptance: EXIT_CODE 0 and an empty output. +- [x] [P2-T9] AC-2 rule report: run `git -C check-ignore -v -- TaskMaster.sln.bak TaskTree/TaskTree.vbproj.bak TaskVisualization/TaskVisualization.vbproj.bak` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t9-ac2-check-ignore-v..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output is three lines, one per path, each beginning `.gitignore:259:*.bak`. The same shape with `--no-index` printed nothing in P0-T10. +- [x] [P2-T10] AC-2 negative control: run `git -C check-ignore -q -- README.md` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t10-ac2-negative-control..md` with the four required fields plus the line `ExpectedExitCode: 1`. Acceptance: EXIT_CODE 1 and an empty output: the P2-T8 command shape returns non-zero for a path that no rule ignores, so its exit 0 is discriminating. +- [x] [P2-T11] AC-2 exact line: run `git -C grep -n -I -F "*.bak" -- .gitignore` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t11-ac2-exact-line..md` with the four required fields. The literal asserted is the exact line `*.bak` that P1-T11 inserted; the two older lines `*.rptproj.bak` and `*.csproj.bak` do not contain the substring `*.bak`. Acceptance: EXIT_CODE 0 and the output is exactly one line, `.gitignore:259:*.bak`. +- [x] [P2-T12] AC-7 README check: with the Grep tool in content mode, search `.github/workflows/README.md` for `_hygiene.yml.*HYGIENE backup-file`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t12-ac7-readme-row..md` with `Timestamp:`, `Command:` (the Grep call), `EXIT_CODE: 0` and `Output Summary:` stating the hit count and the line number only. Acceptance: exactly one hit, on line 24 (the table row; zero hits before P1-T12). +- [x] [P2-T13] Stage the footprint with explicit paths (never `git add -A`): run `git -C add -- .gitignore .github/workflows/README.md scripts/hygiene/Test-RepositoryHygiene.ps1 scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t13-stage-footprint..md` with the four required fields. Staging the feature folder puts the plan and the evidence files written so far in the index; P2-T17 stages the folder again so the artifacts of P2-T14 to P2-T16 are included before the guard scans for absolute host paths. Acceptance: EXIT_CODE 0 and no error output. +- [x] [P2-T14] Base continuity: run `git -C merge-base --is-ancestor HEAD`, then `git -C merge-base origin/main HEAD`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t14-base-continuity..md` with the four required fields for each command. Acceptance: the first command's EXIT_CODE is 0 and the second prints a value equal to `BASE-SHA` recorded in P0-T3. A different value means the branch history was rewritten during the run: stop and report `BASE CHANGED`. +- [x] [P2-T15] Footprint, non-docs paths: run `git -C diff --name-status -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory"` and, as the status companion, `git -C status --porcelain -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory"`. The `.claude/agent-memory` exclusion is needed because agent sessions write tracked memory files that are not product changes. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t15-footprint-non-docs..md` with the four required fields for the diff plus a `Companion Command:` and `Companion Output:` pair for the status command. Acceptance: the diff EXIT_CODE is 0 and the diff lists exactly nine paths: `D` for `TaskMaster.sln.bak`, `TaskTree/TaskTree.vbproj.bak` and `TaskVisualization/TaskVisualization.vbproj.bak`, and `M` for `.gitignore`, `.github/workflows/README.md`, `scripts/hygiene/Test-RepositoryHygiene.ps1`, `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` and `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`. The companion output is the same nine entries with no `??` entry and nothing else. The Git adapter `scripts/hygiene/Test-RepositoryHygiene.Git.ps1` and its test file must not appear. +- [x] [P2-T16] Footprint, feature folder: run `git -C diff --name-status -- docs/features` and, as the status companion, `git -C status --porcelain -- docs/features`. Write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t16-footprint-feature-folder..md` with the four required fields for the diff plus a `Companion Command:` and `Companion Output:` pair. Acceptance: the diff EXIT_CODE is 0 and every listed path and every companion entry either begins with `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/` or equals `docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md`, whose diff status is `A`. The paths that do not begin with the active-folder prefix are exactly the list recorded in P0-T4, and no other path under `docs/` is listed. +- [x] [P2-T17] AC-5 guard over the final tree. First run `git -C add -- docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961` so that the artifacts of P2-T13 to P2-T16 are in the index (the guard enumerates the index and reads working-tree contents, so an unstaged new file is not scanned). Then run `pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE'` and write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/qa-gates/p2-t17-guard-final..md` with the four required fields. Acceptance: the `git add` prints no error, EXIT_CODE of the guard is 0 and its output is exactly the one line `HYGIENE Findings=0`. The same command printed `HYGIENE Findings=3` and exited 1 in P1-T9. A `HYGIENE profile-path` line means a staged file carries an absolute path: redact it, re-stage and re-run this task. Files written after this run are this task's own artifact, the issue.md check-offs of P2-T18 to P2-T24 and the P2-T25 handoff; this run does not scan them, each is subject to the redaction rule, and CI runs the same guard. +- [x] [P2-T18] Check off AC-1 in `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-1:`, and only after the artifacts of P2-T6 and P2-T7 exist and meet their acceptance. Acceptance: a Grep of `issue.md` for `^- \[x\] AC-1:` returns 1 hit. +- [x] [P2-T19] Check off AC-2 in the same `issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-2:`, and only after the artifacts of P2-T8, P2-T9, P2-T10 and P2-T11 exist and meet their acceptance. Acceptance: a Grep of `issue.md` for `^- \[x\] AC-2:` returns 1 hit. +- [x] [P2-T20] Check off AC-3 in the same `issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-3:`, and only after the artifacts of P0-T12, P1-T8 and P1-T9 and the P2-T4 artifact exist and meet their acceptance. Acceptance: a Grep of `issue.md` for `^- \[x\] AC-3:` returns 1 hit. +- [x] [P2-T21] Check off AC-4 in the same `issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-4:`, and only after the artifacts of P1-T4 (the fail-before run), P1-T8 (including its clean-listing passed-testcase count of 1) and P2-T3 exist and meet their acceptance. Acceptance: a Grep of `issue.md` for `^- \[x\] AC-4:` returns 1 hit. +- [x] [P2-T22] Check off AC-5 in the same `issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-5:`, and only after the artifacts of P2-T14, P2-T15, P2-T16 and P2-T17 exist and meet their acceptance. Acceptance: a Grep of `issue.md` for `^- \[x\] AC-5:` returns 1 hit. +- [x] [P2-T23] Check off AC-6 in the same `issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-6:`, and only after the artifacts of P2-T1, P2-T2, P2-T3, P2-T4 and P0-T16 exist and meet their acceptance (the last two record that local coverage of `scripts/hygiene` is not measurable and that CI measures it). Acceptance: a Grep of `issue.md` for `^- \[x\] AC-6:` returns 1 hit. +- [x] [P2-T24] Check off AC-7 in the same `issue.md`: change only `- [ ]` to `- [x]` on the line beginning `- [ ] AC-7:`, and only after the artifacts of P1-T12 and P2-T12 exist and meet their acceptance. Acceptance: a Grep of `issue.md` for `^- \[x\] AC-7:` returns 1 hit. +- [x] [P2-T25] Record the handoff to the orchestrator-owned reduced small-audit: write `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence/other/p2-t25-audit-handoff..md` containing `Timestamp:` and the repository-relative path of every artifact written in P0-T1 through P2-T17. The executor neither delegates nor runs the audit. Acceptance: the artifact exists and lists every path, and the executor stops after writing it. + +### Phase 3 — Review Remediation (CR-1 to CR-3) + +Source: the code review of the Phase 0-2 result and the maintainer directive to remediate related defects inside this item. Three findings, all in files this item already changed. Phases 0-2 stay checked and unchanged; `issue.md` is not edited in this phase (all seven AC lines are already checked and none changes). + +- `` in this phase is the fixed alias for `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961`. Every Phase 3 artifact path is `/evidence//p3-t-..md` with `` one of `regression-testing`, `qa-gates` or `other`, and `` read from the clock at write time (never composed). Every artifact records `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`; an artifact whose command is expected to exit 1 also records `ExpectedExitCode: 1`. The execution rules above (Bash discipline, `` placeholder, PoshQC call shape, junit reading, `BASE-SHA` anchoring) apply unchanged. Evidence names no real host path, drive letter path, machine name or account name. +- PowerShell batch budget: Phase 3 edits only `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` among PowerShell files and creates no PowerShell file. Nothing under `.claude/state/` is touched. `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` is READ-ONLY in Phase 3: its counts were verified against the current tree (31 `Should` lines, 31 `-Because`, 10 `Should -(BeTrue|BeFalse) -Because` lines), and task P3-T8 re-reads them and stops and reports on any mismatch rather than editing. +- Findings (current-tree citations, before any Phase 3 edit). CR-1: `Test-RepositoryHygiene.Tests.ps1` lines 171 to 181, the `It 'reports zero findings for backup-lookalike names'` block, asserts five names (`docs/bak/notes.md`, `notes.bakery`, `notes.bak.md`, `backup`, `docs/features/x/Makefile`) inside one `It` through one loop at line 174, so a failure does not name the lookalike. CR-2: the same file has no `-Because` on the array-equality assertions at lines 167, 178, 190 and 201 or on the exit assertion at line 180; its pre-existing assertions at lines 62, 73, 86, 105, 134, 144, 155 and 157 have the same gap (29 `Should` lines, 16 with `-Because`); the Rules test file has `-Because` on all 31 of its `Should` lines. CR-3: `.gitignore` lines 257 to 259 are `*.rptproj.bak`, `*.csproj.bak`, `*.bak`; the first two match a subset of what the third matches. +- Test counts are mechanical. Pre-remediation: 45 (P2-T3). CR-1 replaces one `It` by one `-ForEach` `It` that Pester expands into five test cases, so the count after Phase 3 is 45 - 1 + 5 = 49; CR-2 and CR-3 add no test. The `It` block counts counted by Grep stay 11 in the orchestration file, 29 in the Rules file and 5 in the Git file. + +- [x] [P3-T1] Handoff to the implementation engineer: write `/evidence/other/p3-t1-remediation-handoff..md` containing `Timestamp:`, the Phase 3 footprint (`tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1`, `.gitignore`, this plan file, and the Phase 3 evidence files), the sentence that `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` is READ-ONLY in Phase 3 and is not in the edit footprint, the binding Bash discipline and the PowerShell batch budget from the Phase 3 preamble, and the sentence that no other file is touched and `issue.md` is not edited. Acceptance: the artifact exists with those elements. +- [x] [P3-T2] Pre-remediation fingerprints: run `git -C hash-object -- tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 .gitignore docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`, then with the Grep tool in count mode count `^` and `\r$` in `.gitignore` and `^` in each of the two test files. Write `/evidence/regression-testing/p3-t2-pre-remediation-fingerprints..md` with the four required fields, a `Hash Before:` block of four lines labelled by file, and the five counts. Acceptance: EXIT_CODE 0, four 40-hex-digit hashes, every test-file count at most 500, and the `.gitignore` `^` count and `\r$` count equal (the file is wholly CRLF, 370 lines when Phase 1 completed). The `.gitignore` hash and line count recorded here are the references for P3-T9, P3-T12 and P3-T13; the `issue.md` hash is the reference for P3-T25. +- [x] [P3-T3] Pre-remediation test run: invoke `mcp__drm-copilot__run_poshqc_test` as stated in the execution rules, then Read lines 1 to 2 of `artifacts/pester/pester-junit.xml` and Grep that file in count mode for ` check-ignore -v -- foo.csproj.bak foo.rptproj.bak foo.bak` and write `/evidence/regression-testing/p3-t4-pre-removal-check-ignore..md` with the four required fields. `git check-ignore -v` reports the last matching pattern, so with `*.bak` on line 259 after both specific lines, every sample is attributed to line 259. The expected success-case output is three lines, one per path in argument order, each beginning with the literal `.gitignore:259:*.bak`. Acceptance: EXIT_CODE 0 and exactly those three lines. If the output attributes a sample to line 257 or 258, stop and report: the specific lines are not shadowed and the CR-3 premise is false. +- [x] [P3-T5] CR-1: with the Edit tool, in `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` replace the block at lines 171 to 181 (`It 'reports zero findings for backup-lookalike names' {` through its closing brace; re-read the file first and locate it by that title) by one data-driven `It` whose title is the text `reports zero findings for the backup-lookalike name` followed by a space and the Pester title template for the hashtable key `Name` (the word Name enclosed in angle brackets), on one line with the `-ForEach` parameter and its opening `@(`. Layout: write one hashtable per line, inside `-ForEach @( ... )`, five hashtables in this order: `docs/bak/notes.md`, `notes.bakery`, `notes.bak.md`, `backup`, `docs/features/x/Makefile`, each of the form `@{ Name = 'docs/bak/notes.md' }` with the value of the respective name, so a line-oriented Grep count of the five name entries is 5. The body keeps the original arrange, act and assert steps for one name: `$script:Listing = ConvertTo-EolListing -Path @($Name)`, `$script:Content[$Name] = 'plain text'`, `$result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader`, then three assertions, each with a double-quoted `-Because` that interpolates `$Name`: `@($result.Lines) | Should -Be @('HYGIENE Findings=0')` (because the lookalike name `$Name` has no final `.bak` extension, so only the findings total is printed), `$result.FindingCount | Should -Be 0`, and `$result.ExitCode | Should -Be 0`. Scoping rule: Pester evaluates `-ForEach` data during discovery, before any `BeforeAll` or `BeforeEach` runs, so the five hashtables hold string literals only and reference no `$script:` variable; the `$Name` variable is injected into the `It` scope at run time, and `ConvertTo-EolListing` (defined in `BeforeAll`) and `$script:Reader` and `$script:Content` (set in `BeforeAll` and `BeforeEach`) are resolved at run time, as in the sibling tests. Write `/evidence/other/p3-t5-cr1-lookalike-split..md` with the four required fields. Acceptance: Grep `reports zero findings for backup-lookalike names` in that file returns 0 hits; Grep `reports zero findings for the backup-lookalike name` returns 1 hit (the new title, quoted in the prose above); Grep `-ForEach` returns 1 hit in that file (the file carries no `-ForEach` before this task); Grep `@\{ Name = '` returns 5 hits; Grep `^\s*It ` in count mode returns 11 (one block replaced by one block); Grep `^` in count mode returns at most 500. +- [x] [P3-T6] CR-2, item-added assertions: with the Edit tool, in `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` add a `-Because` argument stating the expected outcome to the three array-equality assertions of the backup tests that carry none, locating (a) and (c) by their unique argument text and (b) by its enclosing test title `drops a governance-directory backup record before the backup rule runs` (the argument text of (b) also occurs at lines 86, 134 and 178 and is not unique): (a) the assertion expecting `@('HYGIENE backup-file TaskMaster.sln.bak', 'HYGIENE Findings=1')` (line 167 before P3-T5), `-Because 'one tracked backup file yields one backup-file line followed by a findings total of 1'`; (b) the assertion in the governance-directory test expecting `@('HYGIENE Findings=0')` (line 190 before P3-T5, inside the `It` that starts at line 183), `-Because 'the governance-directory backup record is dropped, so only the zero-findings total is printed'`; (c) the assertion in the profile-path test expecting the three lines ending `'HYGIENE Findings=2'` (line 201 before P3-T5), `-Because 'a backup file that also carries a profile path yields the backup-file line, the profile-path line and a findings total of 2'`. The two assertions of the split lookalike test (lines 178 and 180 before P3-T5) are covered by P3-T5. Write `/evidence/other/p3-t6-cr2-backup-test-because..md` with the four required fields. Acceptance: Grep `-Because 'one tracked backup file yields` returns 1 hit, Grep `-Because 'the governance-directory backup record is dropped` returns 1 hit, Grep `-Because 'a backup file that also carries a profile path` returns 1 hit, all in that file. +- [x] [P3-T7] CR-2, pre-existing sibling assertions in the same file (same component, same defect class, so remediated here under the maintainer directive): with the Edit tool, in `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` add a `-Because` argument to each of the eight assertions that carry none, locating each by its unique text: `Should -BeExactly ('HYGIENE profile-path ' + $reported + ':2')` (line 62), `Should -Contain ('HYGIENE raw-document ' + $raw)` (line 73), the `Should -Be @('HYGIENE Findings=0')` assertion in the test `retains a package-level projection record` (line 86), `Should -BeExactly 'HYGIENE Findings=5'` (line 105), the `Should -Be @('HYGIENE Findings=0')` assertion in the test `returns a zero exit decision over clean content` (line 134), the `Should -Be @(('HYGIENE profile-path ' + $leaking + ':3'), 'HYGIENE Findings=1')` assertion (line 144), the `Should -Be @('HYGIENE unreadable ' + $broken)` assertion (line 155) and the `$result.ExitCode | Should -Be 1` assertion in the test `reports a record whose content reader throws as unreadable` (line 157). Each `-Because` is one single-quoted sentence naming the expected outcome (for example `'a retained projection yields only the zero-findings total'`). Assertion logic, order and expected values do not change. Write `/evidence/other/p3-t7-cr2-sibling-because..md` with the four required fields. Acceptance: with the Grep tool in count mode on that file, `Should` returns 29 and `-Because` returns 29 (before P3-T5, P3-T6 and P3-T7 the two counts were 29 and 16); the two counts are equal. +- [x] [P3-T8] CR-2, Rules test file sweep (read-only; the file is never edited in Phase 3): with the Grep tool in count mode on `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`, count `Should` and `-Because`, and count `Should -(BeTrue|BeFalse) -Because` (the ten assertions of the `Test-BackupFilePath` block added by this item). Write `/evidence/qa-gates/p3-t8-cr2-rules-tests-because-sweep..md` with `Timestamp:`, `Command:` (the three Grep calls), `EXIT_CODE: 0` and `Output Summary:` stating the three counts. Acceptance: `Should` 31, `-Because` 31 and the third count 10, so no `-Because` gap exists in the file and the task makes no edit. A different value means the tree differs from the counts verified at planning time (31, 31 and 10): stop and report the mismatch without editing the file. +- [x] [P3-T9] CR-3 negative control, step 1 (remove the covering rule): with the Edit tool, in `.gitignore` delete only the line `*.bak` (line 259), leaving `*.rptproj.bak` (line 257) and `*.csproj.bak` (line 258). Write `/evidence/regression-testing/p3-t9-control-covering-rule-removed..md` with `Timestamp:`, `Command:` (the Edit call and the Read call), `EXIT_CODE: 0` and `Output Summary:`. Acceptance: a Read of `.gitignore` lines 255 to 260 shows `ServiceFabricBackup/`, `*.rptproj.bak`, `*.csproj.bak` and then a blank line; Grep `^\*\.bak` in count mode returns 0; Grep `^` in count mode equals the P3-T2 count minus 1. This is a deliberate temporary state that P3-T12 reverses. +- [x] [P3-T10] CR-3 negative control, step 2 (the check can fail): run `git -C check-ignore -v -- foo.bak` and write `/evidence/regression-testing/p3-t10-control-plain-bak-unignored..md` with the four required fields plus the line `ExpectedExitCode: 1`. With neither specific line matching `foo.bak`, no rule matches. The expected output is empty. Acceptance: EXIT_CODE 1 and an empty output. This shows `*.bak` is the only rule covering a plain `.bak` name and that the P3-T14 check is able to fail. +- [x] [P3-T11] CR-3 negative control, step 3 (the specific lines cover only their own names): run `git -C check-ignore -v -- foo.csproj.bak foo.rptproj.bak` and write `/evidence/regression-testing/p3-t11-control-specific-lines-only..md` with the four required fields. The expected success-case output is two lines in argument order, beginning with the literals `.gitignore:258:*.csproj.bak` and `.gitignore:257:*.rptproj.bak`. Acceptance: EXIT_CODE 0 and exactly those two lines. Together with P3-T10 this shows the two specific lines are not needed for anything `*.bak` covers and are needed for nothing else. +- [x] [P3-T12] CR-3 negative control, step 4 (restore and re-prove the covering rule): with the Edit tool, insert the exact line `*.bak` immediately after the line `*.csproj.bak` in `.gitignore`, then run `git -C hash-object -- .gitignore`. Write `/evidence/regression-testing/p3-t12-control-restored..md` with the four required fields, `Hash After Restore:` and the `.gitignore` hash recorded by P3-T2 as `Hash Before:`. Acceptance: the two hashes are identical (the file is byte-identical to its pre-control state, including line endings), and a Read of lines 255 to 260 shows `*.rptproj.bak`, `*.csproj.bak`, `*.bak` on consecutive lines. If the hashes differ, stop and report: the restore changed the file. +- [x] [P3-T13] CR-3 removal: with the Edit tool, in `.gitignore` delete the two lines `*.rptproj.bak` and `*.csproj.bak` and keep `*.bak`; add no `!` negation line and change no other line. Write `/evidence/other/p3-t13-gitignore-redundant-lines-removed..md` with `Timestamp:`, `Command:` (the Edit call and the verification calls), `EXIT_CODE: 0` and `Output Summary:`. Acceptance: a Read of `.gitignore` lines 255 to 258 shows `UpgradeLog*.htm`, `ServiceFabricBackup/`, `*.bak` and a blank line in that order with no `*.rptproj.bak` or `*.csproj.bak`; Grep `^\*\.(rptproj|csproj)\.bak` in count mode returns 0; Grep `^\*\.bak` in count mode returns 1; Grep `^` in count mode equals the P3-T2 count minus 2, and Grep `\r$` in count mode equals the `^` count (CRLF preserved). +- [x] [P3-T14] CR-3 re-proof of coverage: run `git -C check-ignore -v -- foo.csproj.bak foo.rptproj.bak foo.bak` and write `/evidence/qa-gates/p3-t14-ac2-samples-after-removal..md` with the four required fields. The expected success-case output is three lines in argument order, each beginning with the literal `.gitignore:257:*.bak`. Acceptance: EXIT_CODE 0 and exactly those three lines. P3-T4 printed `.gitignore:259:*.bak` for the same command and P3-T10 printed nothing for the plain name with `*.bak` absent; the change in line number confirms the removal took effect and the covering rule is the one reported. +- [x] [P3-T15] AC-2 exact check after removal: run `git -C check-ignore -q -- TaskMaster.sln.bak` and write `/evidence/qa-gates/p3-t15-ac2-check-ignore-q..md` with the four required fields. The quiet form prints nothing on success. Acceptance: EXIT_CODE 0 and an empty output (the P2-T8 command, re-run on the final `.gitignore`). +- [x] [P3-T16] AC-2 negative control after removal: run `git -C check-ignore -q -- README.md` and write `/evidence/qa-gates/p3-t16-ac2-negative-control..md` with the four required fields plus the line `ExpectedExitCode: 1`. Acceptance: EXIT_CODE 1 and an empty output, so the exit 0 of P3-T15 is discriminating. +- [x] [P3-T17] AC-2 exact line after removal: run `git -C grep -n -I -F "*.bak" -- .gitignore` and write `/evidence/qa-gates/p3-t17-ac2-exact-line..md` with the four required fields. The literal asserted is the line `*.bak`; the two removed lines never contained it, so the line number is the discriminating value: P2-T11 printed line 259 and the final tree prints line 257. Acceptance: EXIT_CODE 0 and the output is exactly one line, `.gitignore:257:*.bak`. +- [x] [P3-T18] Citation scan of earlier evidence and AC-2 text (no edit): with the Grep tool in files-with-matches mode over `/evidence`, using the glob `p[02]-t*.md` and the pattern `\.gitignore:25[0-9]`, list the files that cite a `.gitignore` line number; then Grep `issue.md` for `^- \[x\] AC-2:` (count) and for `^- \[x\] AC-` (count). Write `/evidence/other/p3-t18-citation-scan..md` with `Timestamp:`, `Command:` (the Grep calls), `EXIT_CODE: 0` and `Output Summary:` listing the files and the two counts. Acceptance: the listed files are exactly `p0-t11-check-ignore-control.*.md` (cites line 258), `p2-t9-ac2-check-ignore-v.*.md` and `p2-t11-ac2-exact-line.*.md` (both cite line 259); the AC-2 count is 1 and the `AC-` count is 7. The three listed files are records of the tree as it stood at Phases 0 and 2 and are not edited; the Phase 3 artifacts P3-T14 and P3-T17 supersede their line numbers (257), and the AC-2 text (`.gitignore` carries a `*.bak` rule so `git check-ignore -q` exits 0 for a re-created file) is satisfied by P3-T15 and P3-T17 without edit. The orchestrator-owned audit file `/feature-audit.2026-10-02T05-30.md` also cites line 259 (in its AC-2 row); it lies outside the scanned `evidence` folder, is not edited by Phase 3, and is superseded by the orchestrator's re-review. +- [x] [P3-T19] Format (write-mode tool): run `git -C hash-object -- scripts/hygiene/Test-RepositoryHygiene.ps1 scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 scripts/hygiene/Test-RepositoryHygiene.Git.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1`, invoke `mcp__drm-copilot__run_poshqc_format` as stated in the execution rules, then run the same hash command again. Write `/evidence/qa-gates/p3-t19-format..md` with the four required fields, `ITERATION: `, and `Hash Before:` and `Hash After:` blocks (six lines each). The tool exits ok whether or not it rewrote a file, so the observation is the hash comparison. Acceptance: the tool returned an ok result (EXIT_CODE 0) and the two blocks are identical. Differing blocks mean the formatter rewrote a file (likely the new `-ForEach` block): the artifact is still written, the loop restarts at P3-T19 with `ITERATION` incremented, and the rewritten file is re-checked against the P3-T5 to P3-T7 acceptance Greps. +- [x] [P3-T20] Analyze: invoke `mcp__drm-copilot__run_poshqc_analyze` as stated in the execution rules, then run the same six-file `git -C hash-object` command as P3-T19. Write `/evidence/qa-gates/p3-t20-analyze..md` with the four required fields, `ITERATION: `, and the tool's returned summary sentence quoted verbatim; when the tool returns ok, record exactly this phrase: "PoshQC analyze: ok result; tool reports no finding count". Acceptance: the tool returned an ok result (EXIT_CODE 0) with no reported finding and the six hashes equal the `Hash After:` block of the P3-T19 artifact of the same iteration. +- [x] [P3-T21] Test: invoke `mcp__drm-copilot__run_poshqc_test` as stated in the execution rules, then Read lines 1 to 2 of `artifacts/pester/pester-junit.xml` and Grep that file in count mode for `status="Failed"`, for ``), stop and report. Write `/evidence/qa-gates/p3-t21-test..md` with the four required fields and `ITERATION: `; `Output Summary:` records `tests`, `errors`, `failures`, the Failed count and the two name counts only. Acceptance: `tests="49"` exactly (the 45 of P3-T3 plus four from the CR-1 split), `errors="0"`, `failures="0"`, Failed count 0, the five-name count 5 and the old-name count 0. Pester coverage of `scripts/hygiene` is measured by the CI Pester job on this branch; no local percentage is asserted (P0-T16). +- [x] [P3-T22] Stage the Phase 3 footprint with explicit paths (never `git add -A`): run `git -C add -- .gitignore tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961` and write `/evidence/qa-gates/p3-t22-stage-footprint..md` with the four required fields. Acceptance: EXIT_CODE 0 and no error output. +- [x] [P3-T23] Guard over the final tree: run `pwsh -NoProfile -Command 'Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE'` (a non-isolated executor is required, as for P2-T17) and write `/evidence/qa-gates/p3-t23-guard-final..md` with the four required fields. Acceptance: EXIT_CODE 0 and the output is exactly the one line `HYGIENE Findings=0`. A `HYGIENE profile-path` line means a staged file carries an absolute path: redact it, re-stage with P3-T22 and re-run. Files written after this run are this artifact and the artifacts of P3-T24 to P3-T27; each is subject to the redaction rule and CI runs the same guard. +- [x] [P3-T24] Footprint, non-docs paths: run `git -C diff --name-status -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory"` and, as the status companion, `git -C status --porcelain -- . ":(exclude)docs/features" ":(exclude).claude/agent-memory"`. Write `/evidence/qa-gates/p3-t24-footprint-non-docs..md` with the four required fields for the diff plus a `Companion Command:` and `Companion Output:` pair. Acceptance: the diff EXIT_CODE is 0 and the diff lists exactly the nine paths of P2-T15 with the same statuses (`D` for the three backups; `M` for `.gitignore`, `.github/workflows/README.md`, `scripts/hygiene/Test-RepositoryHygiene.ps1`, `scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` and `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1`); the companion output contains only paths from those nine and no `??` entry. `tests/scripts/hygiene/Test-RepositoryHygiene.Git.Tests.ps1` and `scripts/hygiene/Test-RepositoryHygiene.Git.ps1` must not appear. Phase 3 therefore adds no path to the Phase 2 footprint. +- [x] [P3-T25] Footprint, feature folder: run `git -C diff --name-status -- docs/features`, as the status companion `git -C status --porcelain -- docs/features`, and `git -C hash-object -- docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/issue.md`. Write `/evidence/qa-gates/p3-t25-footprint-feature-folder..md` with the four required fields for the diff, a `Companion Command:` and `Companion Output:` pair, and `Hash After:` for `issue.md`. Acceptance: the diff EXIT_CODE is 0; every listed path and companion entry begins with `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/` or equals `docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md`; the paths outside the active-folder prefix are exactly the list recorded in P0-T4; and the `issue.md` hash equals the hash recorded by P3-T2 (the file is unchanged by Phase 3). +- [x] [P3-T26] Base continuity: run `git -C merge-base --is-ancestor HEAD`, then `git -C merge-base origin/main HEAD`. Write `/evidence/qa-gates/p3-t26-base-continuity..md` with the four required fields for each command. Acceptance: the first command's EXIT_CODE is 0 and the second prints a value equal to `BASE-SHA` recorded in P0-T3. A different value means the branch history was rewritten: stop and report `BASE CHANGED`. +- [x] [P3-T27] Record the handoff to the orchestrator-owned re-review: write `/evidence/other/p3-t27-rereview-handoff..md` containing `Timestamp:` and the repository-relative path of every artifact written in P3-T1 through P3-T26, plus the sentence that the executor neither delegates nor runs the re-review and that no `issue.md` check-off changed in Phase 3. Acceptance: the artifact exists and lists every path, and the executor stops after writing it. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T05-30.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T05-30.md new file mode 100644 index 000000000..243b56b67 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T05-30.md @@ -0,0 +1,192 @@ +# Policy Audit - Issue #961 (remaining tracked backup files and hygiene guard rule C) + +- Timestamp: 2026-10-02T05-30 +- Work Mode: minor-audit (reduced small-audit) +- Base: 94287369908cc920b21b0e3256314f988ad7d2f5 (merge-base with origin/main, verified by P2-T14 evidence and unchanged); Head: f93005d38288020a2b0ebcfd2a51c1d713f7a357 +- Scope: full branch diff against the resolved base + +## Executive Summary + +Verdict: PASS with no blocking findings. The branch deletes the three tracked backup files, adds `*.bak` to `.gitignore`, adds rule C (`Test-BackupFilePath`) to the repository hygiene guard, extends the `_hygiene.yml` row in `.github/workflows/README.md`, and adds 14 Pester tests (31 to 45). Reviewer-run guard output on the final tree is `HYGIENE Findings=0`. The numeric repo-wide Pester LINE figure for `scripts/hygiene` is produced by CI (`_pester.yml`) and is pending; it is not available locally because the PoshQC coverage document does not carry `scripts/hygiene`. Every added executable statement is exercised by a named passing test, so the changed-line obligation is met on local evidence. + +Non-blocking observations: the branch diff contains two orchestrator memory files under `.claude/agent-memory/orchestrator/` that are outside the stated footprint; three evidence documents (`preflight-clearance`, `p1-t1-implementation-handoff`, `p2-t25-audit-handoff`) are handoff notes without an `EXIT_CODE` field; one orchestration test bundles five lookalike names in one `It`. + +## Rejected Scope Narrowing + +- Caller text (verbatim): "Pester line coverage comes from the CI Pester job (pester-coverage JaCoCo artifact), not locally; the PoshQC coverage document does not carry scripts/hygiene, so mark the coverage figure as CI-sourced and pending rather than FAIL." +- Disposition: not a language exclusion, so the full audit proceeds. The numeric figure is recorded as CI-sourced and pending, and an explicit PASS verdict is still recorded on the evidence that exists, because a changed language may not carry a pending-only verdict. +- No other caller text narrowed the scope. + +## Evidence Location Compliance + +- Scanned the branch diff for paths under `artifacts/baselines/`, `artifacts/qa/`, `artifacts/evidence/` and `artifacts/coverage/`: zero paths found. +- All new evidence lives under `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence//` with kinds `baseline`, `other`, `qa-gates` and `regression-testing`. +- `validate_evidence_locations.py` is not present in this worktree; the scan was done by reading the full `git diff --name-status` listing. Result: PASS. +- EVIDENCE_LOCATION_OVERRIDE_REJECTED: none required; no caller instruction supplied a non-canonical path. +- No raw junit, trx or collector XML is tracked by this branch (`git ls-files` shows only pre-existing JaCoCo projections from other features, none added here). + +## 1. General Unit Test Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Independence and determinism | PASS | Rules tests call pure `Test-BackupFilePath` with literal strings. Orchestration tests mock `Invoke-GitExe` (the wrapper seam, never git itself) in `BeforeEach` and reset `$script:Content` and `$script:Listing` per test. | +| No temp files, no network, no external process | PASS | The added tests use in-memory listings and a content delegate; no file system access. | +| Arrange-Act-Assert | PASS | Each added `It` separates setup, one invocation and assertions with blank lines, matching the existing file style. | +| Failure messages (`-Because`) | PASS | All 10 rule tests and 3 of 4 orchestration tests carry `-Because` on the primary assertion. Array-equality assertions on `$result.Lines` carry no `-Because`, but the expected array is self-describing. See code-review finding CR-2. | +| Scenario completeness | PASS | Positive (root, nested, upper-case, bare `.bak`), negative (directory `bak`, `.bakery`, `.bak.md`, `backup`, extensionless, bare `bak`), governance skip, combined rule C plus rule B, clean listing. | +| Negative control | PASS | P1-T4 RED run: 12 failures (10 rule tests plus 2 orchestration tests) before the production change; P1-T8/P2-T3 GREEN: 45 tests, 0 failures, 0 errors. P1-T9 real-tree control printed `HYGIENE Findings=3`, exit 1, with the three files still present. | +| Test file location | PASS | Tests are in `tests/scripts/hygiene/` mirroring `scripts/hygiene/`. | +| Banned determinism APIs | PASS | No `Start-Sleep`, wall-clock or random use in the added tests. | +| Line coverage threshold | PASS (conditional) | See section 5. Numeric figure pending CI. | + +### 1.2.1 Per-Language Coverage Comparison + +- C#: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero C# files changed on this branch. +- TypeScript: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero TypeScript 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. +- PowerShell: Baseline: pending CI measurement. Post-change: pending CI measurement. Change: 6 changed PowerShell files, 14 added tests. New/changed-code coverage: 100%. Disposition: PASS. Evidence: evidence/qa-gates/p2-t4-statement-coverage-map.2026-10-02T05-17.md and evidence/baseline/p0-t16-coverage-limitation.2026-10-02T05-11.md. + +### 1.2.2 Coverage Artifact State + +### Coverage Evidence Checklist + +- C# baseline coverage artifact: `N/A - out of scope` +- C# post-change coverage artifact: `N/A - out of scope` +- TypeScript baseline coverage artifact: `N/A - out of scope` +- TypeScript post-change coverage artifact: `N/A - out of scope` +- PowerShell baseline coverage artifact: `CI _pester.yml run on the base commit (pester-coverage JaCoCo artifact), pending` +- PowerShell post-change coverage artifact: `CI _pester.yml run on the pull request head (pester-coverage JaCoCo artifact), pending` +- Python baseline coverage artifact: `N/A - out of scope` +- Python post-change coverage artifact: `N/A - out of scope` +- Per-language comparison summary: section 1.2.1 of this document + +PowerShell Pester coverage verdict: PASS (conditional) - all 3 added executable statements are exercised by named passing tests; the repo-wide LINE figure is CI-sourced and pending. + +## 2. General Code Change Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Simplicity and separation of concerns | PASS | `Test-BackupFilePath` is a pure one-statement predicate in `Test-RepositoryHygiene.Rules.ps1`; orchestration only calls it. Rule C does not add an exemption mechanism. | +| Reuse | PASS | Same `[System.IO.Path]::GetExtension(...) -ieq` idiom already used for the `.xml` gate in the orchestrator. | +| Error handling | PASS | No new catch blocks; the existing unreadable-file handling is unchanged. | +| Documentation | PASS | Comment-based help on the new function; header comment and `.DESCRIPTION` of `Invoke-RepositoryHygieneMain` updated; README `_hygiene.yml` row updated. | +| File size limit (500 lines) | PASS | Re-read from disk: `Test-RepositoryHygiene.ps1` 93, `.Rules.ps1` 146, `.Tests.ps1` 204, `.Rules.Tests.ps1` 288 lines (P2-T5; the main script was independently read in full by this review at 93 lines). | +| Public API compatibility | PASS | No signature changed; one function added; output format extended by one new line kind. | +| Footprint | PASS (with note) | Non-docs footprint is exactly three deletions and six modifications as required (P2-T15, confirmed by the diff listing). Two extra files are present under `.claude/agent-memory/orchestrator/` (see section 7, row Footprint). | +| Dependencies | PASS | None added. | +| Toolchain loop (format, analyze, test) | PASS | PoshQC format run changed nothing (hash-before equals hash-after, six files); PoshQC analyze: pass (0 findings); tool reports no count; PoshQC test: 45 tests, 0 errors, 0 failures. | + +## 3. Language-Specific Code Change Policy Compliance + +PowerShell (`.claude/rules/powershell.md`): + +| Requirement | Verdict | Evidence | +|---|---|---| +| Advanced function with `CmdletBinding`, mandatory typed parameter, `OutputType` | PASS | `Test-BackupFilePath` declares `[CmdletBinding()]`, `[OutputType([bool])]`, `[Parameter(Mandatory = $true)] [string]$RelativePath`. | +| Approved verb and descriptive noun | PASS | `Test-` verb; PSScriptAnalyzer reported zero findings. | +| PowerShell 7 compatibility | PASS | Uses .NET `System.IO.Path`; the guard run by this review under `pwsh` printed `HYGIENE Findings=0`. | +| No `Invoke-Expression`, secrets or hard-coded paths | PASS | None added. | +| Gates run through PoshQC MCP only | PASS | P2-T1, P2-T2, P2-T3 evidence records the MCP tool calls; no raw `Invoke-Pester` or `Invoke-ScriptAnalyzer`. | +| Mocking rules | PASS | Orchestration tests mock `Invoke-GitExe` with parity signature `param([string[]]$GitArgs)`; git is never mocked directly. | +| Change budget | PASS | Two production PowerShell files plus two test files. | + +Other changed file types: `.gitignore` (one added line, `*.bak`) and the README table row are configuration and documentation changes with no language policy. + +## 4. Language-Specific Unit Test Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Pester v5, `Describe`/`It`, `*.Tests.ps1` naming | PASS | New `Describe 'Test-BackupFilePath'` in `Test-RepositoryHygiene.Rules.Tests.ps1`; four new `It` blocks in `Test-RepositoryHygiene.Tests.ps1`. | +| One behavior per `It` | PASS (note) | One exception: the lookalike-names test asserts five names in one `It`; see code-review finding CR-1. | +| Mock before use, signature parity | PASS | `Mock Invoke-GitExe` in `BeforeEach`. | +| Violating fixtures assembled at run time | PASS | The existing `$script:Violation` concatenation is reused; no contiguous profile path is committed (reviewer sweep of the feature folder and the changed files found none). | +| No temp files | PASS | None. | + +## 5. Test Coverage Detail + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|---|---|---|---|---|---|---| +| PowerShell | 4 | 45 total (31 baseline, 14 added) | PASS | N/A - CI-sourced, pending | N/A - CI-sourced, pending | 100% | +| C# | 0 | N/A | N/A | N/A | N/A | N/A | +| TypeScript | 0 | N/A | N/A | N/A | N/A | N/A | +| Python | 0 | N/A | N/A | N/A | N/A | N/A | + +Coverage artifact state and verdict by language: + +| Language | Coverage artifact | Verdict | Disposition | +|---|---|---|---| +| PowerShell | CI pester-coverage JaCoCo artifact from `_pester.yml` (pending); local PoshQC document carries 13 packages and zero `scripts/hygiene` entries (P0-T16) | PASS (conditional) | Numeric LINE figure to be read from CI after the push | +| C#, TypeScript, Python | none required | not evaluated | Zero changed files for these languages | + +Statement-to-test map (P2-T4, re-derived by this review from the diff): + +- `Test-BackupFilePath` body (one `return` statement): exercised by 10 passing tests; the true result by 4 tests and the false result by 6 tests. +- `if (Test-BackupFilePath ...)` in `Invoke-RepositoryHygieneMain`: true side by the seeded `.sln.bak` control and the `.bak` plus profile-path test; false side by the lookalike and clean-listing tests; the governance skip that precedes it by the governance backup test. +- `$lines.Add('HYGIENE backup-file ' + ...)`: exercised by the two true-side tests. +- Added statements: 3 of 3 exercised (100%). Pester measures command and line coverage only, so no branch figure exists. + +Repo-wide threshold (85% line): not locally measurable for `scripts/hygiene`; CI `_pester.yml` asserts the `LINE` figure at 80 and publishes the JaCoCo document. The orchestrator is to read it after the push and record it against this audit. A figure below the floor would convert the conditional PASS to FAIL. + +Pester breakpoint-binding check: `Test-BackupFilePath` is dot-sourced through the main script from both test files, which is the pattern already used by the existing rule tests, so no new first-parse-copy hazard is introduced. + +## 6. Test Execution Metrics + +| Metric | Baseline | Post-change | Evidence | +|---|---|---|---| +| Tests in `tests/scripts/hygiene` | 31 | 45 | P0-T15, P2-T3 | +| Failures | 0 | 0 | P2-T3 | +| Errors | 0 | 0 | P2-T3 | +| RED run before production change | not applicable | 12 failures, exit 1 | P1-T4 | +| Guard run on tree with the three backups present | `HYGIENE Findings=0` (pre-rule) | `HYGIENE Findings=3`, exit 1 | P0-T12, P1-T9 | +| Guard run on final tree | `HYGIENE Findings=0` | `HYGIENE Findings=0`, exit 0 | P2-T17; reviewer re-run | + +## 7. Code Quality Checks + +| Check | Result | Command / Evidence | +|---|---|---| +| Format | PASS | PoshQC format over `scripts/hygiene` and `tests/scripts/hygiene`; six file hashes unchanged (P2-T1). | +| Analyze | PASS | PoshQC analyze: pass (0 findings); tool reports no count (P2-T2). | +| Test | PASS | PoshQC test: tests=45, errors=0, failures=0 (P2-T3). | +| Guard on final tree | PASS | Reviewer ran the guard under `pwsh` from the worktree root: `HYGIENE Findings=0`, exit 0. | +| AC-1 index check | PASS | `git ls-files -- "*.bak"` printed nothing at HEAD (reviewer re-run); worktree status is clean. | +| No reader of the deleted files | PASS | Reviewer search for `sln.bak` and `vbproj.bak` outside `docs/`, `.claude/` and `artifacts/` returned no match. | +| Evidence artifact shape | PASS (note) | All 47 evidence documents carry `Timestamp:`. 43 command-bearing documents also carry `EXIT_CODE:` and `Output Summary:`. The other 4 (`phase0-instructions-read`, `preflight-clearance`, `p1-t1-implementation-handoff`, `p2-t25-audit-handoff`) are narrative notes without a command. | +| Host path and account sweep | PASS (note) | Evidence uses `` and `` placeholders. The only matches for the account handle are the repository URL in `issue.md` and the promoted record, and `Owner: drmoisan` in the plan header; no drive-letter or profile path is present. | +| Raw junit or collector XML committed | PASS | The branch diff adds no XML file. | +| Tonality | PASS | Executor artifacts are factual; no humor, hyperbole or metaphor found in the changed documents. | +| Suppression scan (added lines) | PASS | No `SuppressMessage` or analyzer-disable attribute added. | +| Workflow change scan | PASS | No workflow YAML changed; only the README table row. | +| Footprint | PASS (note) | Two orchestrator memory files outside the stated footprint: `.claude/agent-memory/orchestrator/MEMORY.md` (one line added) and `.claude/agent-memory/orchestrator/poshqc-gates-observed-outputs-for-scripts-hygiene.md` (new, 17 lines). They sit under the governance directory, which the guard excludes, carry no host paths, and are routine agent-memory commits. Recorded for the orchestrator; not blocking. | + +## Appendix A: Test Inventory + +Added to `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` (`Describe 'Test-BackupFilePath'`, 10 tests): + +1. returns true for a root-level solution backup +2. returns true for a nested project backup +3. returns true for an upper-case extension +4. returns true for a bare dot-bak file name +5. returns false for a directory named bak +6. returns false for a longer extension that begins with bak +7. returns false when bak is not the final extension +8. returns false for the word backup +9. returns false for a path with no extension +10. returns false for the bare word bak + +Added to `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` (`Describe 'Invoke-RepositoryHygieneMain'`, 4 tests): + +1. reports a tracked backup file as a finding and fails the guard +2. reports zero findings for backup-lookalike names +3. drops a governance-directory backup record before the backup rule runs +4. still scans a backup file for a profile path + +## Appendix B: Toolchain Commands Reference + +- Format: `mcp__drm-copilot__run_poshqc_format` with the worktree root and scan folders `scripts/hygiene`, `tests/scripts/hygiene`. +- Analyze: `mcp__drm-copilot__run_poshqc_analyze` with the same scan folders. +- Test: `mcp__drm-copilot__run_poshqc_test` with scan folder `tests/scripts/hygiene`. +- Guard: `pwsh -NoProfile -Command "Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE"`. +- Index check: `git -C ls-files -- "*.bak"`. +- Diff: `git -C diff --name-status 94287369908cc920b21b0e3256314f988ad7d2f5 HEAD`. diff --git a/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T06-18.md b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T06-18.md new file mode 100644 index 000000000..618516eb9 --- /dev/null +++ b/docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/policy-audit.2026-10-02T06-18.md @@ -0,0 +1,184 @@ +# Policy Audit - Issue #961 (re-run after remediation Phase 3) + +- Timestamp: 2026-10-02T06-18 +- Work Mode: minor-audit (reduced small-audit, second cycle) +- Base: 94287369908cc920b21b0e3256314f988ad7d2f5 (`git merge-base origin/main HEAD`; equals `origin/main`); Head: 03f4b37c110222149e8a3f12300de22b60594d4d +- Scope: full branch diff against the resolved base (`git diff origin/main...HEAD`) +- Supersedes for the final tree: `policy-audit.2026-10-02T05-30.md` (not overwritten) + +## Executive Summary + +Verdict: PASS with no blocking findings. Remediation Phase 3 (commits 16202b688 to 03f4b37c1) closed the three prior low findings. CR-1: the bundled lookalike `It` is now one `-ForEach` `It` over five cases, each with a `-Because` that names the case. CR-2: every `Should` assertion in the two modified test files carries `-Because` (29 of 29 and 31 of 31 by count). CR-3: `.gitignore` carries only `*.bak`; `*.rptproj.bak` and `*.csproj.bak` are removed; AC-2 still holds and its negative-control evidence is present. Reviewer-run guard output on the final tree is `HYGIENE Findings=0` (exit 0); `git ls-files -- "*.bak"` prints nothing; the worktree is clean. + +CI-measured coverage for the current head is pending. CI run 36990562362 on the earlier head b00fd1f19 measured `scripts/hygiene` at 94.23 percent line coverage (98 of 104). The diff from that head to the current head touches only `.gitignore` and `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1`, so no production line changed since that measurement. The figure for the current head is read from the CI run that follows the push and is recorded as pending here. + +Non-blocking: two orchestrator memory files remain in the branch diff outside the declared footprint (carried from the first cycle, classified RELATED/Info); `Test-RepositoryHygiene.Git.Tests.ps1`, which this branch does not touch, has 7 assertions without `-Because` (pre-existing, UNRELATED/Info). + +## Rejected Scope Narrowing + +- Caller text (verbatim): "CI-measured coverage is cited as pending (the orchestrator will read the CI run)". +- Disposition: not a language exclusion. The numeric CI figure is recorded as pending for the current head, the earlier-head CI measurement is cited as prior evidence, and an explicit PASS verdict is still recorded for PowerShell on the evidence that exists. +- No other caller text narrowed the scope. The "reduced small-audit" framing is the work-mode artifact set, not a scope reduction; the audit covered the full branch diff. + +## Evidence Location Compliance + +- Scanned the branch diff for paths under `artifacts/baselines/`, `artifacts/qa/`, `artifacts/evidence/` and `artifacts/coverage/`: zero paths found. +- All evidence lives under `docs/features/active/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule-961/evidence//` (kinds `baseline`, `other`, `qa-gates`, `regression-testing`), including the 26 Phase 3 documents. +- `validate_evidence_locations.py` is not present in this worktree; the scan was done on the full `git diff --numstat origin/main...HEAD` listing. Result: PASS. +- EVIDENCE_LOCATION_OVERRIDE_REJECTED: none required. +- No raw junit, trx or collector XML is added by this branch. + +## 1. General Unit Test Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Independence and determinism | PASS | Rules tests call a pure predicate with literal strings. Orchestration tests mock `Invoke-GitExe` (the wrapper seam) in `BeforeEach` and reset `$script:Content` and `$script:Listing` per test; the `-ForEach` cases share no state across cases. | +| No temp files, network or external process | PASS | Tests use in-memory listings and a content delegate; no file system access. | +| Arrange-Act-Assert | PASS | Each added `It`, including the `-ForEach` one, separates setup, one invocation and assertions by blank lines. | +| Failure messages (`-Because`) | PASS | Count of `Should -` lines equals count of `Should -.*-Because` lines in `Test-RepositoryHygiene.Tests.ps1` (29 and 29) and `Test-RepositoryHygiene.Rules.Tests.ps1` (31 and 31). The `-ForEach` assertions interpolate `$Name` in the reason text. See section 4 for the untouched Git test file. | +| Scenario completeness | PASS | Positive, negative, governance skip, combined rule C plus rule B, clean listing; the five lookalike names are now five separately reported cases. | +| Negative control | PASS | Phase 1 RED run (12 failures) and real-tree control (`HYGIENE Findings=3`, exit 1); Phase 3 `.gitignore` negative controls in section 7. | +| Test file location | PASS | `tests/scripts/hygiene/` mirrors `scripts/hygiene/`. | +| Banned determinism APIs | PASS | No sleep, wall-clock or random use in added or changed tests. | +| Line coverage threshold | PASS (conditional) | See section 5. | + +### 1.2.1 Per-Language Coverage Comparison + +- C#: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero C# files changed on this branch. +- TypeScript: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero TypeScript 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. +- PowerShell: Baseline: pending CI measurement for the base commit. Post-change: 94.23 percent line coverage for `scripts/hygiene` at the earlier head b00fd1f19 (CI run 36990562362); current head pending CI. Change: no production PowerShell line changed between that head and the current head. New/changed-code coverage: 100 percent (3 of 3 added statements; the new function file is 32 of 32 in the CI document). Disposition: PASS. Evidence: evidence/other/ci-pester-coverage.2026-10-02T09-45.md and evidence/qa-gates/p2-t4-statement-coverage-map.2026-10-02T05-17.md. + +### 1.2.2 Coverage Artifact State + +### Coverage Evidence Checklist + +- C# baseline coverage artifact: `N/A - out of scope` +- C# post-change coverage artifact: `N/A - out of scope` +- TypeScript baseline coverage artifact: `N/A - out of scope` +- TypeScript post-change coverage artifact: `N/A - out of scope` +- PowerShell baseline coverage artifact: `CI _pester.yml run on the base commit (pester-coverage JaCoCo artifact), pending` +- PowerShell post-change coverage artifact: `CI run 36990562362 pester-coverage JaCoCo artifact at head b00fd1f19 (scripts/hygiene 94.23 percent line); current head 03f4b37c1 pending CI` +- Python baseline coverage artifact: `N/A - out of scope` +- Python post-change coverage artifact: `N/A - out of scope` +- Per-language comparison summary: section 1.2.1 of this document + +PowerShell Pester coverage verdict: PASS (conditional) - production lines unchanged since the CI-measured head at 94.23 percent, all 3 added executable statements are exercised by named passing tests, and the current-head CI figure is pending. + +## 2. General Code Change Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Simplicity and separation of concerns | PASS | `Test-BackupFilePath` is a one-statement pure predicate; orchestration only calls it; no exemption mechanism added. Phase 3 changed no production file. | +| Reuse | PASS | Same `GetExtension ... -ieq` idiom as the existing `.xml` gate. | +| Error handling | PASS | No new catch blocks. | +| Documentation | PASS | Comment-based help on the new function; header comment, `.DESCRIPTION` and README `_hygiene.yml` row updated. | +| File size limit (500 lines) | PASS | Read from disk: `Test-RepositoryHygiene.ps1` 93, `.Rules.ps1` 146, `Test-RepositoryHygiene.Tests.ps1` 209, `.Rules.Tests.ps1` 288. | +| Public API compatibility | PASS | No signature changed; one function and one output line kind added. | +| Footprint | PASS (note) | Non-docs, non-memory footprint is exactly three deletions and six modifications (P3-T24 and reviewer diff). Phase 3 added no path to it. Two orchestrator memory files remain (section 7). | +| Dependencies | PASS | None added. | +| Toolchain loop (format, analyze, test) | PASS | PoshQC format: six file hashes identical before and after (P3-T19). PoshQC analyze: ok result; tool reports no finding count (P3-T20). PoshQC test: tests=49, errors=0, failures=0 (P3-T21). | + +## 3. Language-Specific Code Change Policy Compliance + +PowerShell (`.claude/rules/powershell.md`): + +| Requirement | Verdict | Evidence | +|---|---|---| +| Advanced function, `CmdletBinding`, mandatory typed parameter, `OutputType` | PASS | `Test-BackupFilePath` declaration unchanged from the first cycle. | +| Approved verb | PASS | `Test-`; analyze reported no finding. | +| PowerShell 7 compatibility | PASS | Reviewer ran the guard under `pwsh`: `HYGIENE Findings=0`. | +| Gates through PoshQC MCP only | PASS | P3-T19 to P3-T21 record the MCP tool calls; no raw `Invoke-Pester` or `Invoke-ScriptAnalyzer`. | +| Mocking rules | PASS | `Mock Invoke-GitExe` with `param([string[]]$GitArgs)`; unchanged by Phase 3. | +| Change budget | PASS | Two production PowerShell files plus two test files. | + +Other changed file types: `.gitignore` (net: `*.rptproj.bak` and `*.csproj.bak` replaced by `*.bak`) and the README row are configuration and documentation changes with no language policy. + +## 4. Language-Specific Unit Test Policy Compliance + +| Requirement | Verdict | Evidence | +|---|---|---| +| Pester v5, `Describe`/`It`, `*.Tests.ps1` | PASS | New `Describe 'Test-BackupFilePath'`; five `It` blocks (one `-ForEach`) added to `Test-RepositoryHygiene.Tests.ps1`. | +| One behavior per `It` | PASS | The prior exception (five names in one `It`) is resolved: one `-ForEach` `It` with five hashtables, one per line; PoshQC junit shows five expanded passed names (P3-T21). | +| Mock before use, signature parity | PASS | Unchanged. | +| Violating fixtures assembled at run time | PASS | No contiguous profile path in the changed files; reviewer sweep of the feature folder found only the repository URL in `issue.md`. | +| No temp files | PASS | None. | +| `-Because` on assertions | PASS (note) | Complete in both modified files. `Test-RepositoryHygiene.Git.Tests.ps1` (not in the branch diff) has 15 `Should -` lines and 8 with `-Because`; the 7 gaps pre-date this branch. Classified UNRELATED, Info; no change required by this item. | + +## 5. Test Coverage Detail + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|---|---|---|---|---|---|---| +| PowerShell | 4 | 49 total (31 baseline, 18 added) | PASS | N/A - CI-sourced, pending | 94.23% (scripts/hygiene, CI at head b00fd1f19); current head pending CI | 100% | +| C# | 0 | N/A | N/A | N/A | N/A | N/A | +| TypeScript | 0 | N/A | N/A | N/A | N/A | N/A | +| Python | 0 | N/A | N/A | N/A | N/A | N/A | + +Coverage artifact state and verdict by language: + +| Language | Coverage artifact | Verdict | Disposition | +|---|---|---|---| +| PowerShell | CI pester-coverage JaCoCo artifact (run 36990562362, head b00fd1f19, read); current-head run pending. Local PoshQC document carries no `scripts/hygiene` entry (P0-T16). | PASS (conditional) | Orchestrator reads the current-head CI LINE figure after the push. A figure below 85 percent for a changed file would convert the verdict to FAIL. | +| C#, TypeScript, Python | none required | not evaluated | Zero changed files for these languages | + +Statement-to-test map (re-derived from the diff): `Test-BackupFilePath` return statement exercised by 10 rule tests (true by 4, false by 6); the `if` and the `$lines.Add` in `Invoke-RepositoryHygieneMain` exercised on the true side by the seeded `.sln.bak` control and the `.bak` plus profile-path test, on the false side by the five lookalike cases and the clean listing; the governance skip by the governance backup test. Added statements: 3 of 3 (100 percent). Pester reports no branch figure. + +Per-file CI figures at head b00fd1f19 (production unchanged since): `Test-RepositoryHygiene.Rules.ps1` 32 of 32 (100 percent); `Test-RepositoryHygiene.ps1` 32 of 35 (91.43 percent; `Invoke-RepositoryHygieneMain` 28 of 28, the 3 missed lines are in the script-level entry). Both meet the 85 percent line floor. The Phase 3 test split does not reduce coverage of any changed line: the five names that previously ran in one `It` still run, each as its own case. + +## 6. Test Execution Metrics + +| Metric | Baseline | Post-change | Evidence | +|---|---|---|---| +| Tests in `tests/scripts/hygiene` | 31 | 49 | P0-T15, P3-T21 | +| Failures | 0 | 0 | P3-T21 | +| Errors | 0 | 0 | P3-T21 | +| Tests after Phase 2 | 45 | 49 (the lookalike `It` became five cases) | P3-T21 | +| Guard run on tree with the three backups present | `HYGIENE Findings=0` (pre-rule) | `HYGIENE Findings=3`, exit 1 | P0-T12, P1-T9 | +| Guard run on final tree | `HYGIENE Findings=0` | `HYGIENE Findings=0`, exit 0 | P3-T23; reviewer re-run | +| CI Pester at head b00fd1f19 | not applicable | Passed=407 Failed=0, LinePercent=94.64 | evidence/other/ci-pester-coverage.2026-10-02T09-45.md | + +## 7. Code Quality Checks + +| Check | Result | Command / Evidence | +|---|---|---| +| Format | PASS | PoshQC format, six hashes unchanged (P3-T19). | +| Analyze | PASS | PoshQC analyze: ok result; tool reports no finding count (P3-T20). | +| Test | PASS | PoshQC test: tests=49, errors=0, failures=0 (P3-T21). | +| Guard on final tree | PASS | Reviewer ran the guard under `pwsh` from the worktree root: `HYGIENE Findings=0`. | +| AC-1 index check | PASS | `git ls-files -- "*.bak"` prints nothing at HEAD; `git status --short` is empty. | +| AC-2 after CR-3 | PASS | `.gitignore` line 257 is `*.bak` and the only `.bak` rule in the file (reviewer read lines 250 to 261). Reviewer `git check-ignore -q TaskMaster.sln.bak` exits 0. P3-T14: three names attributed to `.gitignore:257:*.bak`. P3-T16: `README.md` exits 1 (discriminating). | +| CR-3 negative controls | PASS | P3-T9 and P3-T10: with `*.bak` removed, `foo.bak` matches no rule and `check-ignore` exits 1, so the check can fail and `*.bak` is the covering rule. P3-T11: the two specific lines cover only their own names. P3-T12: restoration byte-identical (hash before equals hash after, `e0c040d9...`). | +| No reader of the deleted files | PASS | First-cycle search unchanged; no build input names them. | +| Evidence artifact shape | PASS (note) | Command-bearing Phase 3 documents carry `Timestamp:`, `Command:`, `EXIT_CODE:`, `Output Summary:`; expected non-zero commands carry `ExpectedExitCode:`. Handoff notes (`p3-t1`, `p3-t27`) carry no command. | +| Host path and account sweep | PASS | Regex sweep over the feature folder (excluding the three audit artifacts) for drive letters, profile paths and the account handle returned one match: the repository URL in `issue.md` line 10. Evidence uses `` and ``. | +| Raw junit or collector XML committed | PASS | None added. | +| Tonality | PASS | Phase 3 artifacts and the changed test text are factual; no humor, hyperbole or metaphor found. | +| Suppression scan | PASS | None added. | +| Workflow change scan | PASS | No workflow YAML changed. | +| Footprint | PASS (note) | `.claude/agent-memory/orchestrator/MEMORY.md` (modified) and `poshqc-gates-observed-outputs-for-scripts-hygiene.md` (added) remain outside the declared footprint. Governance-directory content, excluded by the guard, no host paths. RELATED, Info; the orchestrator confirms they belong in the pull request. | +| Issue.md untouched in Phase 3 | PASS | `issue.md` shows 7 of 7 criteria checked; Phase 3 evidence states no check-off changed. | + +## Appendix A: Test Inventory + +Added to `tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1` (`Describe 'Test-BackupFilePath'`, 10 tests): root-level solution backup; nested project backup; upper-case extension; bare dot-bak name; directory named bak; `.bakery`; `.bak.md`; the word backup; no extension; bare word bak. + +Added to `tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1` (`Describe 'Invoke-RepositoryHygieneMain'`, 4 `It` blocks, 8 executed tests): + +1. reports a tracked backup file as a finding and fails the guard +2. reports zero findings for the backup-lookalike name `` (`-ForEach`, five cases: `docs/bak/notes.md`, `notes.bakery`, `notes.bak.md`, `backup`, `docs/features/x/Makefile`) +3. drops a governance-directory backup record before the backup rule runs +4. still scans a backup file for a profile path + +Baseline 31 plus 10 plus 1 plus 5 plus 1 plus 1 = 49. + +## Appendix B: Toolchain Commands Reference + +- Format: `mcp__drm-copilot__run_poshqc_format` with the worktree root and scan folders `scripts/hygiene`, `tests/scripts/hygiene`. +- Analyze: `mcp__drm-copilot__run_poshqc_analyze` with the same scan folders. +- Test: `mcp__drm-copilot__run_poshqc_test` with scan folder `tests/scripts/hygiene`. +- Guard: `pwsh -NoProfile -Command "Set-Location ; & ./scripts/hygiene/Test-RepositoryHygiene.ps1; exit $LASTEXITCODE"`. +- Index check: `git -C ls-files -- "*.bak"`. +- Ignore check: `git -C check-ignore -q TaskMaster.sln.bak`. +- Diff: `git -C diff origin/main...HEAD`. diff --git a/docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md b/docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md new file mode 100644 index 000000000..9c22e197f --- /dev/null +++ b/docs/features/potential/promoted/2026-10-01-remaining-tracked-backup-files-and-hygiene-guard-rule.md @@ -0,0 +1,68 @@ +# remaining-tracked-backup-files-and-hygiene-guard-rule (Issue #961) + +- Date captured: 2026-10-01 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/remaining-tracked-backup-files-and-hygiene-guard-rule/ (Issue #961) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #961 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/961 +- Last Updated: 2026-10-01 +## Summary + +#951 (PR #960) removed the eight tracked `*.csproj.bak` files and added an ignore rule. Two gaps remain: +- three other tracked backup files on `main`; +- no CI rule preventing a forced add from reintroducing them. + +An ignore rule does not stop `git add -f`. + +## Environment + +- OS/version: n/a +- Python version: n/a +- Command/flags used: `git ls-tree -r --name-only origin/main | grep -E "\.bak$"` +- Data source or fixture: repository tree at `9a3d2dd3e` or later + +## Steps to Reproduce + +1. List tracked `.bak` files on `main`: + - `TaskMaster.sln.bak` + - `TaskTree/TaskTree.vbproj.bak` + - `TaskVisualization/TaskVisualization.vbproj.bak` +2. Force-add any `*.csproj.bak`. The hygiene guard reports nothing. + +## Expected Behavior + +- No tracked backup files. +- The repository hygiene guard (#927, `scripts/hygiene/`) fails CI when any tracked `*.bak` exists. + +## Actual Behavior + +Three backups remain, and the guard has no backup-file rule. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: #951 executor follow-ups 1 and 2 (PR #960). + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [ ] Medium +- [x] Low + +## Suspected Cause / Notes + +#951 was scoped to `*.csproj.bak` only. Confirm that no build step reads the `.sln.bak` or `.vbproj.bak` files before deleting them. + +## Proposed Fix / Validation Ideas + +- [ ] Delete the three files, and broaden the ignore rule to `*.bak` if appropriate. +- [ ] Add a guard rule that flags tracked `*.bak` paths, with Pester tests and a negative control proving the rule fails on a seeded path. Run it through PoshQC, with coverage from CI. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 b/scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 index a3ff6437a..a733b734b 100644 --- a/scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 +++ b/scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 @@ -121,3 +121,26 @@ function Get-RawEvidenceDocumentKind { return 'none' } + +function Test-BackupFilePath { + <# + .SYNOPSIS + Tests whether a tracked path is a backup file. + .DESCRIPTION + A path is a backup file when its final extension equals .bak, compared case-insensitively. + Only the final extension is compared, so a directory named bak, a longer extension such as + .bakery and a path whose final extension is another value are not backup files. + .PARAMETER RelativePath + The repository-relative path of the tracked file. + .OUTPUTS + System.Boolean + #> + [CmdletBinding()] + [OutputType([bool])] + param( + [Parameter(Mandatory = $true)] + [string]$RelativePath + ) + + return [System.IO.Path]::GetExtension($RelativePath) -ieq '.bak' +} diff --git a/scripts/hygiene/Test-RepositoryHygiene.ps1 b/scripts/hygiene/Test-RepositoryHygiene.ps1 index 5f9bf9a17..e36a55d86 100644 --- a/scripts/hygiene/Test-RepositoryHygiene.ps1 +++ b/scripts/hygiene/Test-RepositoryHygiene.ps1 @@ -1,6 +1,7 @@ # Repository hygiene guard. Enumerates the tracked files through git ls-files and fails when any # tracked file outside the governance directory is a raw test-platform or raw coverage-collector -# document (rule A) or contains a line matching the generic user-profile path pattern (rule B). +# document (rule A), contains a line matching the generic user-profile path pattern (rule B), or +# is a backup file whose final extension is .bak (rule C). # Finding lines carry the rule name, the path and a line number only, never the matched text, so # the CI log cannot echo an identifier. The guard carries no exemption mechanism of any kind. @@ -12,11 +13,12 @@ Set-StrictMode -Version Latest function Invoke-RepositoryHygieneMain { <# .SYNOPSIS - Applies both hygiene rules to every tracked file and returns the findings and exit decision. + Applies the hygiene rules to every tracked file and returns the findings and exit decision. .DESCRIPTION Drops records under the governance directory by a path-prefix test, classifies each remaining record with Get-RawEvidenceDocumentKind (content is passed only for the xml - extension), and scans its decoded text with Find-UserProfilePathMatch. Emits + extension), and scans its decoded text with Find-UserProfilePathMatch. A record whose final + extension is .bak yields "HYGIENE backup-file " and is still content-scanned. Emits "HYGIENE raw-document ", "HYGIENE profile-path :" once per file with the first matching line, "HYGIENE unreadable " when the content delegate throws, and a final "HYGIENE Findings=" line. Writes nothing to any stream. @@ -39,6 +41,10 @@ function Invoke-RepositoryHygieneMain { continue } + if (Test-BackupFilePath -RelativePath $record.Path) { + $lines.Add('HYGIENE backup-file ' + $record.Path) + } + $text = $null $isUnreadable = $false try { diff --git a/tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 b/tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 index 3a1aeac8c..db1ded330 100644 --- a/tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 +++ b/tests/scripts/hygiene/Test-RepositoryHygiene.Rules.Tests.ps1 @@ -220,3 +220,69 @@ $fixture = @" $kind | Should -Be 'none' -Because 'the extension gate keeps non-XML files out of rule A' } } + +Describe 'Test-BackupFilePath' { + BeforeAll { + . (Join-Path -Path $PSScriptRoot -ChildPath '../../../scripts/hygiene/Test-RepositoryHygiene.ps1') + } + + It 'returns true for a root-level solution backup' { + $result = Test-BackupFilePath -RelativePath 'TaskMaster.sln.bak' + + $result | Should -BeTrue -Because 'a final .bak extension marks a backup file' + } + + It 'returns true for a nested project backup' { + $result = Test-BackupFilePath -RelativePath 'TaskTree/TaskTree.vbproj.bak' + + $result | Should -BeTrue -Because 'the directory depth does not change the final extension' + } + + It 'returns true for an upper-case extension' { + $result = Test-BackupFilePath -RelativePath 'Notes.BAK' + + $result | Should -BeTrue -Because 'the extension comparison is case-insensitive' + } + + It 'returns true for a bare dot-bak file name' { + $result = Test-BackupFilePath -RelativePath '.bak' + + $result | Should -BeTrue -Because 'a file named .bak has the final extension .bak' + } + + It 'returns false for a directory named bak' { + $result = Test-BackupFilePath -RelativePath 'docs/bak/notes.md' + + $result | Should -BeFalse -Because 'only the final extension of the path is compared, not a directory segment' + } + + It 'returns false for a longer extension that begins with bak' { + $result = Test-BackupFilePath -RelativePath 'notes.bakery' + + $result | Should -BeFalse -Because 'the extension must equal .bak exactly' + } + + It 'returns false when bak is not the final extension' { + $result = Test-BackupFilePath -RelativePath 'notes.bak.md' + + $result | Should -BeFalse -Because 'the final extension is .md' + } + + It 'returns false for the word backup' { + $result = Test-BackupFilePath -RelativePath 'backup' + + $result | Should -BeFalse -Because 'a name without a dot has no extension' + } + + It 'returns false for a path with no extension' { + $result = Test-BackupFilePath -RelativePath 'docs/features/x/Makefile' + + $result | Should -BeFalse -Because 'a path with no extension is not a backup file' + } + + It 'returns false for the bare word bak' { + $result = Test-BackupFilePath -RelativePath 'bak' + + $result | Should -BeFalse -Because 'the word bak without a leading dot is not an extension' + } +} diff --git a/tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 b/tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 index 3ce0af103..c987262b3 100644 --- a/tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 +++ b/tests/scripts/hygiene/Test-RepositoryHygiene.Tests.ps1 @@ -59,7 +59,7 @@ Describe 'Invoke-RepositoryHygieneMain' { $findings = @($result.Lines | Where-Object { $_ -like 'HYGIENE profile-path *' }) $findings.Count | Should -Be 1 -Because 'the governance-directory record is dropped before any rule runs' - $findings[0] | Should -BeExactly ('HYGIENE profile-path ' + $reported + ':2') + $findings[0] | Should -BeExactly ('HYGIENE profile-path ' + $reported + ':2') -Because 'the reported record carries its profile path on line 2' $result.FindingCount | Should -Be 1 -Because 'only the docs Markdown record is a violation' } @@ -70,7 +70,7 @@ Describe 'Invoke-RepositoryHygieneMain' { $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader - @($result.Lines) | Should -Contain ('HYGIENE raw-document ' + $raw) + @($result.Lines) | Should -Contain ('HYGIENE raw-document ' + $raw) -Because 'a raw document is reported by path' $result.FindingCount | Should -Be 1 -Because 'a raw document produces exactly one finding' $result.ExitCode | Should -Be 1 -Because 'a raw document fails the guard' } @@ -83,7 +83,7 @@ Describe 'Invoke-RepositoryHygieneMain' { $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader $result.FindingCount | Should -Be 0 -Because 'a package-level projection is a permitted evidence form' - @($result.Lines) | Should -Be @('HYGIENE Findings=0') + @($result.Lines) | Should -Be @('HYGIENE Findings=0') -Because 'a retained projection yields only the zero-findings total' } It 'returns a non-zero exit decision when findings exist' { @@ -102,7 +102,7 @@ Describe 'Invoke-RepositoryHygieneMain' { $result.FindingCount | Should -Be 5 -Because 'one profile-path line and four raw documents are reported' @($result.Lines | Where-Object { $_ -like 'HYGIENE raw-document *' }).Count | Should -Be 4 -Because 'TestRun and CoverageSession roots and both collector extensions are raw documents' $result.ExitCode | Should -Be 1 -Because 'the exit decision is one when the findings total is non-zero' - @($result.Lines)[-1] | Should -BeExactly 'HYGIENE Findings=5' + @($result.Lines)[-1] | Should -BeExactly 'HYGIENE Findings=5' -Because 'the last line is the findings total of 5' } It 'returns a zero exit decision over clean content' { @@ -131,7 +131,7 @@ Describe 'Invoke-RepositoryHygieneMain' { $result.FindingCount | Should -Be 0 -Because 'placeholders in any encoding, empty text, null text and non-raw XML carry no violation' $result.ExitCode | Should -Be 0 -Because 'the exit decision is zero when the findings total is zero' - @($result.Lines) | Should -Be @('HYGIENE Findings=0') + @($result.Lines) | Should -Be @('HYGIENE Findings=0') -Because 'clean content yields only the zero-findings total' } It 'prints path and line only and never the matched text' { @@ -141,7 +141,7 @@ Describe 'Invoke-RepositoryHygieneMain' { $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader - @($result.Lines) | Should -Be @(('HYGIENE profile-path ' + $leaking + ':3'), 'HYGIENE Findings=1') + @($result.Lines) | Should -Be @(('HYGIENE profile-path ' + $leaking + ':3'), 'HYGIENE Findings=1') -Because 'the output names the path and the line number and then the findings total' @($result.Lines | Where-Object { $_ -like ('*' + $script:Segment + '*') }).Count | Should -Be 0 -Because 'no output line may echo the matched text' } @@ -152,8 +152,58 @@ Describe 'Invoke-RepositoryHygieneMain' { $result = Invoke-RepositoryHygieneMain -ReadContent $throwingReader - @($result.Lines | Where-Object { $_ -like 'HYGIENE unreadable *' }) | Should -Be @('HYGIENE unreadable ' + $broken) + @($result.Lines | Where-Object { $_ -like 'HYGIENE unreadable *' }) | Should -Be @('HYGIENE unreadable ' + $broken) -Because 'a record whose reader throws is reported as unreadable by path' $result.FindingCount | Should -Be 1 -Because 'an unreadable record is a finding, never skipped silently' - $result.ExitCode | Should -Be 1 + $result.ExitCode | Should -Be 1 -Because 'an unreadable record fails the guard' + } + + It 'reports a tracked backup file as a finding and fails the guard' { + $backup = 'TaskMaster.sln.bak' + $script:Listing = ConvertTo-EolListing -Path @($backup) + $script:Content[$backup] = 'plain text' + + $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader + + @($result.Lines) | Should -Be @('HYGIENE backup-file TaskMaster.sln.bak', 'HYGIENE Findings=1') -Because 'one tracked backup file yields one backup-file line followed by a findings total of 1' + $result.ExitCode | Should -Be 1 -Because 'a tracked backup file fails the guard' + } + + It 'reports zero findings for the backup-lookalike name ' -ForEach @( + @{ Name = 'docs/bak/notes.md' } + @{ Name = 'notes.bakery' } + @{ Name = 'notes.bak.md' } + @{ Name = 'backup' } + @{ Name = 'docs/features/x/Makefile' } + ) { + $script:Listing = ConvertTo-EolListing -Path @($Name) + $script:Content[$Name] = 'plain text' + + $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader + + @($result.Lines) | Should -Be @('HYGIENE Findings=0') -Because "the lookalike name $Name has no final .bak extension, so only the findings total is printed" + $result.FindingCount | Should -Be 0 -Because "the lookalike name $Name has no final .bak extension" + $result.ExitCode | Should -Be 0 -Because "the lookalike name $Name yields no finding, so the guard passes" + } + + It 'drops a governance-directory backup record before the backup rule runs' { + $governed = '.claude/agent-memory/notes.bak' + $script:Listing = ConvertTo-EolListing -Path @($governed) + $script:Content[$governed] = 'plain text' + + $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader + + @($result.Lines) | Should -Be @('HYGIENE Findings=0') -Because 'the governance-directory backup record is dropped, so only the zero-findings total is printed' + $result.ExitCode | Should -Be 0 -Because 'the governance skip runs ahead of the backup rule' + } + + It 'still scans a backup file for a profile path' { + $backup = 'docs/features/x/old.bak' + $script:Listing = ConvertTo-EolListing -Path @($backup) + $script:Content[$backup] = $script:Violation + + $result = Invoke-RepositoryHygieneMain -ReadContent $script:Reader + + @($result.Lines) | Should -Be @('HYGIENE backup-file docs/features/x/old.bak', 'HYGIENE profile-path docs/features/x/old.bak:1', 'HYGIENE Findings=2') -Because 'a backup file that also carries a profile path yields the backup-file line, the profile-path line and a findings total of 2' + $result.ExitCode | Should -Be 1 -Because 'both the backup-file rule and the profile-path rule fire' } }