diff --git a/.github/workflows/_build-analyzers.yml b/.github/workflows/_build-analyzers.yml index 74f274293..7cc083186 100644 --- a/.github/workflows/_build-analyzers.yml +++ b/.github/workflows/_build-analyzers.yml @@ -38,24 +38,12 @@ jobs: with: path: packages key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }} - # The bare-prefix fallback below is safe against stale package - # versions: `nuget restore` (next step) always runs unconditionally - # and is idempotent per package for packages.config-style restores — - # each package is materialized under a version-qualified directory - # (packages/{id}.{version}/, matching every HintPath in this repo's - # .csproj files). A fallback cache populated under an older - # packages.config hash can therefore only ever contribute either - # (a) version-folders that still match the current packages.config - # (a legitimate, desired reuse) or (b) inert orphaned version- - # folders for packages no longer referenced by any HintPath. Either - # way, `nuget restore` fetches exactly the delta implied by the - # current packages.config from the network before the build step - # runs, so a fallback hit can never cause the build to compile - # against a package version other than the one packages.config - # names. See docs/features/active/2026-09-02-ci-build-infra-debt-730/ - # research/ for the full analysis (issue #730). - restore-keys: | - nuget-${{ runner.os }}- + # No restore-keys fallback (issue #936). A fallback cache restored under an + # older packages.config hash carries superseded packages/{id}.{version}/ + # folders, and those folders satisfy stale project-file paths (for example an + # left on an old version by a Dependabot bump). That hid a + # clean-restore compile failure (CS0006) from every required check. An exact-key + # miss now restores from scratch, so CI builds only what the manifests declare. - name: Restore solution shell: pwsh diff --git a/.github/workflows/_build-nullable.yml b/.github/workflows/_build-nullable.yml index 0d68921bb..4f0ce7edf 100644 --- a/.github/workflows/_build-nullable.yml +++ b/.github/workflows/_build-nullable.yml @@ -38,24 +38,12 @@ jobs: with: path: packages key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }} - # The bare-prefix fallback below is safe against stale package - # versions: `nuget restore` (next step) always runs unconditionally - # and is idempotent per package for packages.config-style restores — - # each package is materialized under a version-qualified directory - # (packages/{id}.{version}/, matching every HintPath in this repo's - # .csproj files). A fallback cache populated under an older - # packages.config hash can therefore only ever contribute either - # (a) version-folders that still match the current packages.config - # (a legitimate, desired reuse) or (b) inert orphaned version- - # folders for packages no longer referenced by any HintPath. Either - # way, `nuget restore` fetches exactly the delta implied by the - # current packages.config from the network before the build step - # runs, so a fallback hit can never cause the build to compile - # against a package version other than the one packages.config - # names. See docs/features/active/2026-09-02-ci-build-infra-debt-730/ - # research/ for the full analysis (issue #730). - restore-keys: | - nuget-${{ runner.os }}- + # No restore-keys fallback (issue #936). A fallback cache restored under an + # older packages.config hash carries superseded packages/{id}.{version}/ + # folders, and those folders satisfy stale project-file paths (for example an + # left on an old version by a Dependabot bump). That hid a + # clean-restore compile failure (CS0006) from every required check. An exact-key + # miss now restores from scratch, so CI builds only what the manifests declare. - name: Restore solution shell: pwsh diff --git a/.github/workflows/_mstest-coverage.yml b/.github/workflows/_mstest-coverage.yml index f2c84c2ac..0d7b5c3b7 100644 --- a/.github/workflows/_mstest-coverage.yml +++ b/.github/workflows/_mstest-coverage.yml @@ -54,24 +54,12 @@ jobs: with: path: packages key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.config') }} - # The bare-prefix fallback below is safe against stale package - # versions: `nuget restore` (next step) always runs unconditionally - # and is idempotent per package for packages.config-style restores — - # each package is materialized under a version-qualified directory - # (packages/{id}.{version}/, matching every HintPath in this repo's - # .csproj files). A fallback cache populated under an older - # packages.config hash can therefore only ever contribute either - # (a) version-folders that still match the current packages.config - # (a legitimate, desired reuse) or (b) inert orphaned version- - # folders for packages no longer referenced by any HintPath. Either - # way, `nuget restore` fetches exactly the delta implied by the - # current packages.config from the network before the build step - # runs, so a fallback hit can never cause the build to compile - # against a package version other than the one packages.config - # names. See docs/features/active/2026-09-02-ci-build-infra-debt-730/ - # research/ for the full analysis (issue #730). - restore-keys: | - nuget-${{ runner.os }}- + # No restore-keys fallback (issue #936). A fallback cache restored under an + # older packages.config hash carries superseded packages/{id}.{version}/ + # folders, and those folders satisfy stale project-file paths (for example an + # left on an old version by a Dependabot bump). That hid a + # clean-restore compile failure (CS0006) from every required check. An exact-key + # miss now restores from scratch, so CI builds only what the manifests declare. - name: Restore solution shell: pwsh diff --git a/QuickFiler.Test/QuickFiler.Test.csproj b/QuickFiler.Test/QuickFiler.Test.csproj index 82318fa81..db9a1488e 100644 --- a/QuickFiler.Test/QuickFiler.Test.csproj +++ b/QuickFiler.Test/QuickFiler.Test.csproj @@ -526,8 +526,8 @@ - - + + @@ -551,7 +551,7 @@ - + diff --git a/QuickFiler/QuickFiler.csproj b/QuickFiler/QuickFiler.csproj index 3986766e6..7371cdfec 100644 --- a/QuickFiler/QuickFiler.csproj +++ b/QuickFiler/QuickFiler.csproj @@ -604,7 +604,7 @@ - + diff --git a/SVGControl.Test/SVGControl.Test.csproj b/SVGControl.Test/SVGControl.Test.csproj index f3ec4d4d5..0649dc194 100644 --- a/SVGControl.Test/SVGControl.Test.csproj +++ b/SVGControl.Test/SVGControl.Test.csproj @@ -339,8 +339,8 @@ - - + + diff --git a/Tags.Test/Tags.Test.csproj b/Tags.Test/Tags.Test.csproj index 234d8c2dc..a7cad49b2 100644 --- a/Tags.Test/Tags.Test.csproj +++ b/Tags.Test/Tags.Test.csproj @@ -301,8 +301,8 @@ - - + + @@ -325,7 +325,7 @@ - + diff --git a/Tags/Tags.csproj b/Tags/Tags.csproj index 82f0201d8..dd04efd96 100644 --- a/Tags/Tags.csproj +++ b/Tags/Tags.csproj @@ -94,7 +94,7 @@ - + diff --git a/TaskMaster.Test/TaskMaster.Test.csproj b/TaskMaster.Test/TaskMaster.Test.csproj index 758e074c4..f78a3bc91 100644 --- a/TaskMaster.Test/TaskMaster.Test.csproj +++ b/TaskMaster.Test/TaskMaster.Test.csproj @@ -382,8 +382,8 @@ - - + + @@ -406,7 +406,7 @@ - + diff --git a/TaskMaster/TaskMaster.csproj b/TaskMaster/TaskMaster.csproj index 5c5a8c8ff..6a2f22a32 100644 --- a/TaskMaster/TaskMaster.csproj +++ b/TaskMaster/TaskMaster.csproj @@ -572,7 +572,7 @@ - + diff --git a/TaskTree.Test/TaskTree.Test.csproj b/TaskTree.Test/TaskTree.Test.csproj index 11059d2d9..fd5e773ac 100644 --- a/TaskTree.Test/TaskTree.Test.csproj +++ b/TaskTree.Test/TaskTree.Test.csproj @@ -302,8 +302,8 @@ - - + + @@ -326,7 +326,7 @@ - + diff --git a/TaskTree/TaskTree.csproj b/TaskTree/TaskTree.csproj index c55fb8966..40aa65bd8 100644 --- a/TaskTree/TaskTree.csproj +++ b/TaskTree/TaskTree.csproj @@ -97,7 +97,7 @@ - + diff --git a/TaskVisualization.Test/TaskVisualization.Test.csproj b/TaskVisualization.Test/TaskVisualization.Test.csproj index ed0a18ad1..589d1d1f4 100644 --- a/TaskVisualization.Test/TaskVisualization.Test.csproj +++ b/TaskVisualization.Test/TaskVisualization.Test.csproj @@ -326,8 +326,8 @@ - - + + @@ -350,7 +350,7 @@ - + diff --git a/TaskVisualization/TaskVisualization.csproj b/TaskVisualization/TaskVisualization.csproj index 50d5dea7e..a5d85e7ef 100644 --- a/TaskVisualization/TaskVisualization.csproj +++ b/TaskVisualization/TaskVisualization.csproj @@ -147,7 +147,7 @@ - + diff --git a/ToDoModel.Test/ToDoModel.Test.csproj b/ToDoModel.Test/ToDoModel.Test.csproj index 4fa969442..bf724d84f 100644 --- a/ToDoModel.Test/ToDoModel.Test.csproj +++ b/ToDoModel.Test/ToDoModel.Test.csproj @@ -344,8 +344,8 @@ - - + + @@ -368,7 +368,7 @@ - + diff --git a/ToDoModel/ToDoModel.csproj b/ToDoModel/ToDoModel.csproj index eb2b98f1c..fb6795646 100644 --- a/ToDoModel/ToDoModel.csproj +++ b/ToDoModel/ToDoModel.csproj @@ -186,7 +186,7 @@ - + diff --git a/UtilitiesCS.Test/UtilitiesCS.Test.csproj b/UtilitiesCS.Test/UtilitiesCS.Test.csproj index eb1f26811..9e9b50a4f 100644 --- a/UtilitiesCS.Test/UtilitiesCS.Test.csproj +++ b/UtilitiesCS.Test/UtilitiesCS.Test.csproj @@ -978,8 +978,8 @@ - - + + @@ -1003,7 +1003,7 @@ - + diff --git a/UtilitiesCS/UtilitiesCS.csproj b/UtilitiesCS/UtilitiesCS.csproj index a0f7e60d5..5a0968a06 100644 --- a/UtilitiesCS/UtilitiesCS.csproj +++ b/UtilitiesCS/UtilitiesCS.csproj @@ -1313,7 +1313,7 @@ - + diff --git a/VBFunctions.Test/VBFunctions.Test.csproj b/VBFunctions.Test/VBFunctions.Test.csproj index 6c83f1619..6d186bb17 100644 --- a/VBFunctions.Test/VBFunctions.Test.csproj +++ b/VBFunctions.Test/VBFunctions.Test.csproj @@ -279,8 +279,8 @@ - - + + @@ -303,7 +303,7 @@ - + diff --git a/VBFunctions/VBFunctions.csproj b/VBFunctions/VBFunctions.csproj index fd8a3f478..d2a1fe9b9 100644 --- a/VBFunctions/VBFunctions.csproj +++ b/VBFunctions/VBFunctions.csproj @@ -55,7 +55,7 @@ - + diff --git a/docs/features/active/2026-09-29-stale-analyzer-include-paths-break-clean-build-936/issue.md b/docs/features/active/2026-09-29-stale-analyzer-include-paths-break-clean-build-936/issue.md new file mode 100644 index 000000000..6cdd88fed --- /dev/null +++ b/docs/features/active/2026-09-29-stale-analyzer-include-paths-break-clean-build-936/issue.md @@ -0,0 +1,71 @@ +# stale-analyzer-include-paths-break-clean-build (Issue #936) + +- Date captured: 2026-09-29 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/stale-analyzer-include-paths-break-clean-build/ (Issue #936) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #936 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/936 +- Last Updated: 2026-09-29 +- Work Mode: minor-audit + +## Summary + +`main` does not compile after a clean package restore. Dependabot PR #921 (merged 2026-09-26) bumped Meziantou.Analyzer to 3.0.290 and MSTest.Analyzers to 4.4.1 in every `packages.config`, but left the `` paths in the project files pointing at the previous version folders. A clean restore does not create those folders, so the compiler fails with CS0006. CI stays green because its `actions/cache` `restore-keys` fallback restores an older `packages/` cache that still contains the stale folders. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: not applicable (.NET Framework 4.8.1 packages.config solution) +- Command/flags used: `msbuild TaskMaster.sln /t:Restore /p:RestorePackagesConfig=true`, then `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU"` +- Data source or fixture: a fresh detached worktree at `main` `dcce3c816` with an empty `packages/` folder + +## Steps to Reproduce + +1. Create a fresh worktree at `main`. +2. Restore packages into its empty `packages/` folder. +3. Rebuild the solution. + +## Expected Behavior + +The solution builds from a clean restore, and CI fails whenever it would not. + +## Actual Behavior + +The build fails with: +- `CSC : error CS0006: Metadata file '..\packages\Meziantou.Analyzer.3.0.235\analyzers\dotnet\roslyn5.0\cs\Meziantou.Analyzer.dll' could not be found`, in 16 project files. The manifests declare 3.0.290. +- `CSC : error CS0006: Metadata file '..\packages\MSTest.Analyzers.4.4.0\analyzers\dotnet\cs\MSTest.Analyzers.dll' could not be found`, plus `MSTest.Analyzers.CodeFixes.dll`, in 9 project files. The manifests declare 4.4.1. + +A scan of every `..\packages\\` reference against the clean restore finds exactly these two stale directories. It also finds `altcover.8.6.45`, which is guarded by `Exists()` and tracked by #929. + +## Logs / Screenshots + +- [x] Attached minimal logs or screenshot +- Snippet: see Actual Behavior. The fix commit records the before and after rebuild output. + +## Impact / Severity + +- [x] Blocker +- [ ] High +- [ ] Medium +- [ ] Low + +Blocker: the maintainer cannot build the tool from a clean checkout. Every fresh worktree used by agent runs is affected as well. + +## Suspected Cause / Notes + +- The Dependabot grouped update rewrites `packages.config` versions but does not rewrite `` paths. This is the same defect class #898 fixed (PR #913) for the previous Meziantou bump. +- Four workflows (`_build-analyzers.yml`, `_build-nullable.yml`, `_format-check.yml`, `_mstest-coverage.yml`) key the `packages/` cache on `hashFiles('**/packages.config')` with a `restore-keys` prefix fallback. On a miss, the fallback cache carries the old version folders, so stale paths resolve in CI and the defect is invisible to every required check. + +## Proposed Fix / Validation Ideas + +- [ ] Repoint all `` paths to the versions declared in each project's `packages.config`: Meziantou.Analyzer 3.0.290 and MSTest.Analyzers 4.4.1. +- [ ] Remove the `restore-keys` fallback from the `packages/` cache in the four workflows, so CI restores exactly what the manifests declare. +- [ ] Validation: a clean-restore rebuild of the whole solution passes locally on a fresh worktree, and a scan of `..\packages\\` references finds no missing directory other than the `Exists()`-guarded altcover import. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/active/2026-09-29-stale-analyzer-include-paths-break-clean-build-936/plan.2026-09-29T11-19.md b/docs/features/active/2026-09-29-stale-analyzer-include-paths-break-clean-build-936/plan.2026-09-29T11-19.md new file mode 100644 index 000000000..f1eff47ff --- /dev/null +++ b/docs/features/active/2026-09-29-stale-analyzer-include-paths-break-clean-build-936/plan.2026-09-29T11-19.md @@ -0,0 +1,44 @@ +# stale-analyzer-include-paths-break-clean-build (Plan) + +- **Issue:** #936 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Last Updated:** 2026-09-29T11-19 +- **Status:** Draft +- **Version:** 0.1 + +**Fail-closed evidence rule:** Include explicit baseline artifact tasks, final-QA artifact tasks, and coverage-comparison tasks for each in-scope language when policy requires coverage. If any required baseline artifact, QA artifact, or coverage-comparison artifact is missing, the audit verdict must be BLOCKED or INCOMPLETE, never PASS. + +**Evidence accounting rule:** Record the expected artifact path or location in each evidence-producing task. Do not mark evidence-backed work complete without the artifact. + + +**Phase 0 — Context & Inputs** +- [ ] [P0-T1] Link approved spec: +- [ ] [P0-T2] Record branch/commit baseline: +- [ ] [P0-T3] List required environment/fixtures/data: + +**Phase 1 — Preparation** +- [ ] [P1-T1] Confirm scope is locked for this fix (no open spec gaps) +- [ ] [P1-T2] Sync workspace to target branch and ensure tooling is available + +**Phase 2 — Regression Test (must fail first)** +- [ ] [P2-T1] [expect-fail] Add a small, deterministic regression test in the standard module file (use `tests/bugs//#936-.py` only if no clear home exists) +- [ ] [P2-T2] [expect-fail] Run the regression to confirm it fails and captures the repro + +**Phase 3 — Minimal Fix** +- [ ] [P3-T1] Apply the smallest change needed to make the regression test pass; avoid opportunistic refactors + +**Phase 4 — Verification Loop** +- [ ] [P4-T1] Re-run repro and regression test to confirm expected behavior +- [ ] [P4-T2] Run formatter → linter → type checker → tests; restart loop if any step changes files or fails +- [ ] [P4-T3] Record baseline, post-change, and comparison artifact paths for each in-scope language where coverage is required + +**Phase 5 — Documentation & Status** +- [ ] [P5-T1] Update spec/issue with outcomes, decisions, and any deviations from scope + +**Phase 6 — PR & Handoff** +- [ ] [P6-T1] Prepare PR notes (summary, risks, validation performed, links to tests) and request review + +**Phase 7 — Rollout / Follow-up** +- [ ] [P7-T1] Capture deployment/rollout notes and post-fix monitoring items +- [ ] [P7-T2] Record links (issue, PRs, related docs) for traceability diff --git a/docs/features/potential/promoted/2026-09-29-stale-analyzer-include-paths-break-clean-build.md b/docs/features/potential/promoted/2026-09-29-stale-analyzer-include-paths-break-clean-build.md new file mode 100644 index 000000000..f8eaf636b --- /dev/null +++ b/docs/features/potential/promoted/2026-09-29-stale-analyzer-include-paths-break-clean-build.md @@ -0,0 +1,69 @@ +# stale-analyzer-include-paths-break-clean-build (Issue #936) + +- Date captured: 2026-09-29 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/stale-analyzer-include-paths-break-clean-build/ (Issue #936) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #936 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/936 +- Last Updated: 2026-09-29 +## Summary + +`main` does not compile after a clean package restore. Dependabot PR #921 (merged 2026-09-26) bumped Meziantou.Analyzer to 3.0.290 and MSTest.Analyzers to 4.4.1 in every `packages.config`, but left the `` paths in the project files pointing at the previous version folders. A clean restore does not create those folders, so the compiler fails with CS0006. CI stays green because its `actions/cache` `restore-keys` fallback restores an older `packages/` cache that still contains the stale folders. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: not applicable (.NET Framework 4.8.1 packages.config solution) +- Command/flags used: `msbuild TaskMaster.sln /t:Restore /p:RestorePackagesConfig=true`, then `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU"` +- Data source or fixture: a fresh detached worktree at `main` `dcce3c816` with an empty `packages/` folder + +## Steps to Reproduce + +1. Create a fresh worktree at `main`. +2. Restore packages into its empty `packages/` folder. +3. Rebuild the solution. + +## Expected Behavior + +The solution builds from a clean restore, and CI fails whenever it would not. + +## Actual Behavior + +The build fails with: +- `CSC : error CS0006: Metadata file '..\packages\Meziantou.Analyzer.3.0.235\analyzers\dotnet\roslyn5.0\cs\Meziantou.Analyzer.dll' could not be found`, in 16 project files. The manifests declare 3.0.290. +- `CSC : error CS0006: Metadata file '..\packages\MSTest.Analyzers.4.4.0\analyzers\dotnet\cs\MSTest.Analyzers.dll' could not be found`, plus `MSTest.Analyzers.CodeFixes.dll`, in 9 project files. The manifests declare 4.4.1. + +A scan of every `..\packages\\` reference against the clean restore finds exactly these two stale directories. It also finds `altcover.8.6.45`, which is guarded by `Exists()` and tracked by #929. + +## Logs / Screenshots + +- [x] Attached minimal logs or screenshot +- Snippet: see Actual Behavior. The fix commit records the before and after rebuild output. + +## Impact / Severity + +- [x] Blocker +- [ ] High +- [ ] Medium +- [ ] Low + +Blocker: the maintainer cannot build the tool from a clean checkout. Every fresh worktree used by agent runs is affected as well. + +## Suspected Cause / Notes + +- The Dependabot grouped update rewrites `packages.config` versions but does not rewrite `` paths. This is the same defect class #898 fixed (PR #913) for the previous Meziantou bump. +- Four workflows (`_build-analyzers.yml`, `_build-nullable.yml`, `_format-check.yml`, `_mstest-coverage.yml`) key the `packages/` cache on `hashFiles('**/packages.config')` with a `restore-keys` prefix fallback. On a miss, the fallback cache carries the old version folders, so stale paths resolve in CI and the defect is invisible to every required check. + +## Proposed Fix / Validation Ideas + +- [ ] Repoint all `` paths to the versions declared in each project's `packages.config`: Meziantou.Analyzer 3.0.290 and MSTest.Analyzers 4.4.1. +- [ ] Remove the `restore-keys` fallback from the `packages/` cache in the four workflows, so CI restores exactly what the manifests declare. +- [ ] Validation: a clean-restore rebuild of the whole solution passes locally on a fresh worktree, and a scan of `..\packages\\` references finds no missing directory other than the `Exists()`-guarded altcover import. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch