From 03c74ff7868df8290af044b0b6ece96e17b897e1 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:17:30 -0400 Subject: [PATCH 01/14] docs(953): initialize active feature folder for stale Fizzler binding redirects --- .../issue.md | 77 +++++++++++++++++++ .../plan.2026-10-02T00-16.md | 44 +++++++++++ ...le-fizzler-and-unsafe-binding-redirects.md | 7 +- 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md rename docs/features/potential/{ => promoted}/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md (95%) diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md new file mode 100644 index 000000000..b8a17ed3d --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md @@ -0,0 +1,77 @@ +# stale-fizzler-and-unsafe-binding-redirects (Potential Bug) + +- Date captured: 2026-08-04 +- Author: Dan Moisan +- Status: Draft + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Work Mode: minor-audit + +## Summary + +Two families of `app.config` binding redirects name assembly versions that are not deployed. Twelve project configs redirect `Fizzler` to `1.3.0.0` while the deployed assembly is `1.3.1.0`, and `SVGControl/app.config` redirects `System.Runtime.CompilerServices.Unsafe` to `6.0.2.0` while the deployed assembly is `6.0.3.0` and all sixteen sibling configs say `6.0.3.0`. This is the same defect class as bug #418, where a redirect to a non-deployed `ExCSS` version caused `SvgDocument.Open` to fail in hosts that apply the redirect. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- .NET/framework: .NET Framework 4.8.1 (`net481`), WinForms + VSTO +- Command/flags used: static inspection of `*/app.config` against `packages/` and against assembly metadata +- Data source or fixture: the repository's own `app.config` set and restored `packages/` tree + +## Steps to Reproduce + +Verified 2026-08-04 on branch `bug/svg-renderer-null-document-nre-418` at commit `296eac95`: + +1. `grep -rl 'name="Fizzler"' --include=app.config .` returns **13** files. Of their redirects, **12** read `newVersion="1.3.0.0"` and **1** reads `newVersion="1.3.1.0"`. +2. The only deployed Fizzler is `packages/Fizzler.1.3.1/`, and `[System.Reflection.AssemblyName]::GetAssemblyName('packages\Fizzler.1.3.1\lib\netstandard2.0\Fizzler.dll').Version` returns **`1.3.1.0`**. No `1.3.0.0` assembly exists anywhere in the repository. +3. Enumerating `System.Runtime.CompilerServices.Unsafe` redirects across all seventeen project configs: sixteen read `newVersion="6.0.3.0"`; `SVGControl/app.config` alone reads `newVersion="6.0.2.0"`. +4. `SVGControl/bin/Debug/System.Runtime.CompilerServices.Unsafe.dll` is assembly version **`6.0.3.0`**, and both `SVGControl` and `SVGControl.Test` pin package version `6.1.2`. + +## Expected Behavior + +Every `bindingRedirect` `newVersion` names an assembly version that is actually deployed to the output directory, so a host that honors the redirect can satisfy the bind. + +## Actual Behavior + +Twelve Fizzler redirects and one `Unsafe` redirect name versions that exist nowhere in the repository. A host that applies these redirects would request an assembly that cannot be found. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: no runtime failure captured. Both findings are currently latent — see below. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [ ] Medium +- [x] Low + +Both findings are latent today, which is why they were deferred rather than folded into #418: + +- **Fizzler is inert.** Research during #418 established that nothing in the deployed graph carries a `Fizzler` assembly reference. `Svg 3.4.7` does not reference it (its CSS selector work goes through ExCSS `StylesheetParser`, and the `Fizzler` string is absent from `Svg.dll`), and `ExCSS 4.3.1` does not reference it. The `using Fizzler;` at `SVGControl/PictureBoxSVG.cs:14` is unused and emits no `AssemblyRef`. With no requesting reference, the redirect is never consulted. +- **The `Unsafe` outlier is masked.** `SVGControl` is a library, so the redirect in `SVGControl/app.config` is not the one the CLR reads at runtime; the host's config governs, and every host config in the repository says `6.0.3.0`. + +The severity is Low on current evidence, not on principle. Either becomes live the moment a dependency starts carrying the corresponding reference — which is precisely how #418 arose, and #418 was rated High. + +## Suspected Cause / Notes + +Package updates advanced the deployed assembly versions without a corresponding sweep of the `bindingRedirect` values. PR #419 moved `ExCSS` to 4.3.2, `Svg` to 3.4.8, and `Unsafe` to 6.1.2; the `SVGControl` `Unsafe` redirect was left at `6.0.2.0` and the Fizzler redirects at `1.3.0.0`. + +Worth considering as part of the fix: a mechanical check that every `bindingRedirect` `newVersion` in the repository resolves to an assembly version present under `packages/`. That would have caught #418's `ExCSS 4.2.4.0` redirect — a version that existed nowhere — before it reached a designer session, and it would catch the next instance without anyone having to notice by hand. A manual version sweep will not, since sibling-config agreement is exactly what let the `SVGControl` `Unsafe` outlier persist unnoticed. + +The single Fizzler config already at `1.3.1.0` should be identified, since it may indicate a partial fix already attempted. + +## Proposed Fix / Validation Ideas + +- [ ] Unit coverage areas: a test over the repository's `app.config` set asserting every `bindingRedirect` `newVersion` matches a deployed assembly version. Language choice depends on where it lands — a Pester test under `tests/scripts/` carries the PoshQC toolchain and the `>= 85%` line / `>= 75%` branch floors per `.claude/rules/powershell.md`. +- [ ] Integration scenario to retest: open a form hosting `PictureBoxSVG` in the WinForms designer after the sweep, confirming no regression in the #418 fix path. +- [ ] Manual verification notes: confirm the twelve Fizzler redirects and the one `Unsafe` outlier, then re-verify each edited config against its deployed assembly version rather than against its sibling configs. + +Referred here from #418, which scoped both out explicitly: "Fizzler binding redirects" and "`System.Runtime.CompilerServices.Unsafe` redirects in any project other than `SVGControl.Test`". #418's `evidence/baseline/` artifacts and its research artifact carry the supporting assembly-metadata analysis. + +## Next Step + +- [ ] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md new file mode 100644 index 000000000..fc42ea59e --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -0,0 +1,44 @@ +# 2026-08-04-stale-fizzler-and-unsafe-binding-redirects (Plan) + +- **Issue:** #953 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Last Updated:** 2026-10-02T00-16 +- **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//#953-.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/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md b/docs/features/potential/promoted/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md similarity index 95% rename from docs/features/potential/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md rename to docs/features/potential/promoted/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md index dc607806b..e79a4919d 100644 --- a/docs/features/potential/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md +++ b/docs/features/potential/promoted/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md @@ -1,11 +1,14 @@ -# stale-fizzler-and-unsafe-binding-redirects (Potential Bug) +# stale-fizzler-and-unsafe-binding-redirects (Issue #953) - Date captured: 2026-08-04 - Author: Dan Moisan -- Status: Draft +- Status: Promoted -> docs/features/active/stale-fizzler-and-unsafe-binding-redirects/ (Issue #953) > Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. +- Issue: #953 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/953 +- Last Updated: 2026-09-30 ## Summary Two families of `app.config` binding redirects name assembly versions that are not deployed. Twelve project configs redirect `Fizzler` to `1.3.0.0` while the deployed assembly is `1.3.1.0`, and `SVGControl/app.config` redirects `System.Runtime.CompilerServices.Unsafe` to `6.0.2.0` while the deployed assembly is `6.0.3.0` and all sixteen sibling configs say `6.0.3.0`. This is the same defect class as bug #418, where a redirect to a non-deployed `ExCSS` version caused `SvgDocument.Open` to fail in hosts that apply the redirect. From ace898896f526b65000e271fa817d3b320ea60b2 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:21:32 -0400 Subject: [PATCH 02/14] docs(953): add research for Fizzler redirect remedy and redirect gate --- ...irect-remedy-and-redirect-gate-research.md | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md new file mode 100644 index 000000000..7724029c3 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md @@ -0,0 +1,138 @@ +# Research: Fizzler redirect remedy and bindingRedirect gate (issue #953) + +- Date: 2026-10-02 +- Scope: research only; no source changes. All paths are repository-relative to the worktree unless marked "main checkout". +- Evidence tags: [V] verified by Grep/Read/Glob in this session; [U] not verified. + +## 1. Current state (re-verified) + +### 1.1 Fizzler redirects: 13 configs, 2 correct, 11 stale [V] + +Every `name="Fizzler"` block in `**/app.config` (Grep with -A1): + +| Config | newVersion | Line | +|---|---|---| +| SVGControl/app.config | 1.3.1.0 | 14-15 | +| UtilitiesCS/app.config | 1.3.1.0 | 51-52 | +| QuickFiler/app.config | 1.3.0.0 | 50-51 | +| QuickFiler.Test/app.config | 1.3.0.0 | 46-47 | +| TaskMaster/app.config | 1.3.0.0 | 50-51 | +| Tags/app.config | 1.3.0.0 | 46-47 | +| TaskTree/app.config | 1.3.0.0 | 46-47 | +| TaskVisualization/app.config | 1.3.0.0 | 46-47 | +| TaskVisualization.Test/app.config | 1.3.0.0 | 46-47 | +| ToDoModel/app.config | 1.3.0.0 | 51-52 | +| ToDoModel.Test/app.config | 1.3.0.0 | 46-47 | +| UtilitiesCS.Test/app.config | 1.3.0.0 | 46-47 | +| SVGControl.Test/app.config | 1.3.0.0 | 18-19 | + +Stale ones carry `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"`; the two correct ones carry `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`. The issue text says "twelve" stale and one at 1.3.1.0; the current tree has 11 stale and 2 at 1.3.1.0 (SVGControl was fixed by #929 task P1-T5, `docs/features/active/2026-09-28-package-manifest-consistency-residuals-929/evidence/qa-gates/p1-t5-redirects-fixed.2026-09-28T20-01.md:13`). The "single config already at 1.3.1.0" the issue asks to identify is therefore UtilitiesCS (original) plus SVGControl (fixed by #929); not a partial attempt aimed at this issue. + +### 1.2 Unsafe: all 17 configs at 6.0.3.0 [V] + +Grep of `System.Runtime.CompilerServices.Unsafe` -A1 over `**/app.config` returns 17 blocks, every one `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"` (SVGControl/app.config:18-19 included). 17 equals the config-file count from `assemblyIdentity name=` count-mode (17 files listed). The Unsafe half of the issue is fixed. + +### 1.3 Fizzler in the project graph [V] + +- `SVGControl/SVGControl.csproj:58-59` and `UtilitiesCS/UtilitiesCS.csproj:65-66`: ` -DeployedVersionProvider ` returning findings `{AssemblyName, NewVersion, DeployedVersions[]}` plus an examined count (the repo pattern guards a zero finding count with an examined count: `RepositoryTreeConsistency.Tests.ps1:87-89`). The provider is a delegate taking an assembly name and returning the set of deployed assembly versions, mirroring `AssemblyIdentityProvider` (`Repair-PackageManifestConsistency.ps1:39-41`, default at `:117-133`). Policy for a name the provider returns nothing for: report as "unverifiable" separately, not as a failure, so transitive-only redirects do not need an allow list; the real-repo test asserts the unverifiable count is bounded and that the four named families (Fizzler, ExCSS, Svg, Unsafe) are all verifiable. + +Tests (negative control required by the issue): (1) fixture config with Fizzler redirect `1.3.0.0` and provider reporting `1.3.1.0` yields one finding; (2) the same config with `1.3.1.0` yields none; (3) a name with no provider data is unverifiable, not a failure; (4) range `oldVersion` ignored, only `newVersion` compared; (5) a block without bindingRedirect is skipped (parser already yields empty `NewVersion`). The repo-level test then builds the provider from every `*.csproj` `Reference Include="Name, Version=..."` text, runs the detector over all 17 configs, and asserts zero findings. The negative control must flip the provider or fixture, not the production code (memory note: negative control must isolate the code fix). + +## 4. Source of truth for the deployed version (Q5) + +| Source | CI feasible in the Pester job | Reproduces #418 ExCSS / Fizzler classes | Notes | +|---|---|---|---| +| (i) assembly metadata under `packages/` | No. `.github/workflows/_pester.yml` has no checkout of packages, no `nuget restore`; it runs only `tests/scripts/dependencies`, `hygiene`, `vscode` (lines 16-47). Restore + cache exists only in `_mstest-coverage.yml:46-66`, `_build-*.yml`, `dependabot-repair.yml:64-73`. Worktree has no `packages/` either. | Yes, ground truth | Usable locally and in `Repair-PackageManifestConsistency.ps1` default delegate only. | +| (ii) csproj `Reference Include="Name, Version=..."` | Yes (tracked text) | Yes, provided the csproj Reference reflects the deployed assembly, which `Resolve-ReferenceAssemblyVersion` (`ProjectConsistency.psm1:125-167`) and the repair tool keep in agreement with the restored package | Fizzler: both Reference entries at 1.3.1.0 vs 11 redirects at 1.3.0.0 => 11 findings. ExCSS: Reference 4.3.2.0 across csproj lines (e.g. `UtilitiesCS.csproj:62`). | +| (iii) packages.config version mapped to assembly version | Yes | No: not equal quantities | Unsafe package 6.1.2 vs assembly 6.0.3.0 (issue.md:29; csproj Reference `Version=6.0.3.0`, e.g. `SVGControl.csproj:82`); Svg package 3.4.8 vs assembly 3.4.0.0 (`UtilitiesCS.csproj:293`, redirect `QuickFiler/app.config:234-235` is 3.4.0.0). Comparing against package version would produce false findings. | + +Chosen: (ii), with (i) kept as the repair-time truth. The chain is then packages.config <-> HintPath folder (existing verifier) <-> Reference version (existing repair/resolve) <-> redirect newVersion (new gate). Limitation: a redirect whose assembly appears in no csproj Reference is unverifiable under (ii); the number of such names across the 1176 `assemblyIdentity` entries was not enumerated here [U] and must be measured by the planner before fixing the unverifiable policy. All 13 Fizzler, all 17 ExCSS-and-Unsafe-relevant, and the one Svg redirect do have csproj Reference entries (ExCSS refs: SVGControl.Test, UtilitiesCS, SVGControl, QuickFiler csproj lines found; Unsafe in 17 projects' references per the grep). + +CI coverage: Pester CI gate is `$linePercent -lt 80` (`_pester.yml:71`) over `scripts/dependencies` (line 45), whereas `.claude/rules/powershell.md` and `general-unit-test.md` state >= 85%. A new module under `scripts/dependencies` joins the denominator; plan to the stricter 85%. + +## 5. Numeric Derivation Evidence + +Claim A: 13 configs carry a Fizzler redirect; 11 name 1.3.0.0. +- Complete Family: all `app.config` files in the repository (17). +- Exhaustive Search Scope: Glob `**/app.config` via Grep over worktree; `bin/obj/packages` pruned by glob match of the file name only (no app.config under packages in the worktree). +- Inclusion Rules: a `dependentAssembly` with `assemblyIdentity name="Fizzler"`. +- Exclusion Rules: none. +- Primary Search Strategy: Grep `name="Fizzler"` with -A1 over `**/app.config`. +- Primary Member Set: UtilitiesCS.Test, Tags, TaskVisualization.Test, SVGControl.Test, TaskVisualization, TaskTree, TaskMaster, SVGControl, UtilitiesCS, ToDoModel.Test, QuickFiler.Test, ToDoModel, QuickFiler. +- Primary Count: 13 (11 at 1.3.0.0, 2 at 1.3.1.0). +- Cross-check Strategy: Grep `Fizzler` over `**/*.{config,csproj,cs,props,targets}` filtered to config hits. +- Cross-check Member Set: QuickFiler.Test, QuickFiler, TaskMaster, UtilitiesCS.Test, Tags, SVGControl.Test, SVGControl, UtilitiesCS, ToDoModel.Test, ToDoModel, TaskVisualization.Test, TaskVisualization, TaskTree. +- Cross-check Count: 13. +- Member-set Comparison: identical sets. + +Claim B: 17 configs, all Unsafe at 6.0.3.0. +- Primary: Grep `System.Runtime.CompilerServices.Unsafe` -A1 over `**/app.config`: 17 blocks, all `6.0.3.0`. Cross-check: Grep count of `assemblyIdentity name="` per app.config: 17 files. Member sets agree (VBFunctions.Test, TaskVisualization.Test, SVGControl.Test, TaskVisualization, SVGControl, TaskTree.Test, TaskMaster.Test, ToDoModel, QuickFiler, TaskTree, TaskMaster, QuickFiler.Test, Tags.Test, UtilitiesCS.Test, Tags, UtilitiesCS, ToDoModel.Test = 17). + +## 6. File footprint and budget (Q6) + +Sweep (11 files, exact paths): `QuickFiler/app.config`, `QuickFiler.Test/app.config`, `TaskMaster/app.config`, `Tags/app.config`, `TaskTree/app.config`, `TaskVisualization/app.config`, `TaskVisualization.Test/app.config`, `ToDoModel/app.config`, `ToDoModel.Test/app.config`, `UtilitiesCS.Test/app.config`, `SVGControl.Test/app.config`. Edit only the two attributes on the Fizzler line pair (`oldVersion` upper bound and `newVersion`). + +PowerShell: +- New: `scripts/dependencies/BindingRedirectVerification.psm1` (production, 1 slot). +- New: `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` (1 test slot). +- Modified: `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152 lines; adds the all-17-configs real-file test; second test slot). Optionally replace the SVGControl-only test at lines 92-110 or leave it. +- Total: 1 production file, 2 test files: inside the direct-mode cap (2 production) and per-batch cap (3/3) in `.claude/rules/powershell.md`. If the session-wide budget counter has no free production slot, use the fallback (modify `ProjectConsistency.psm1` + `RepositoryTreeConsistency.Tests.ps1`: 1 production modified, 1 test modified, zero new files), at the cost of the header ownership split. The budget state itself is [U]; this run cannot read it. + +No C# change; no `.csproj` change. + +## 7. Risks (Q7) + +- Line endings: app.configs are CRLF (`\r$` match counts equal roughly line counts per file, e.g. SVGControl/app.config 23, TaskMaster/app.config 430) [V]; edits must preserve CRLF. Whether a BOM exists was not conclusively checked [U]. Use byte-preserving edits (Edit tool on exact attribute text), not a rewrite via a text writer that normalizes endings. `Invoke-BindingRedirectReconciliation` itself preserves terminators by construction (`ProjectConsistency.psm1:18-23`), and is a usable mechanical alternative to hand edits (calling it with `Fizzler`/`1.3.1.0`). +- Formatter: `.csharpierignore:17-18` excludes `**/app.config` and `**/packages.config`, so `csharpier check` neither requires nor reformats these edits. `.csproj/.props/.targets` also excluded (lines 12-14). +- Build impact: redirects are runtime-only; a wide edit does not change compilation. `dependabot-repair.yml:135` stages `*/app.config`, so later bot runs will touch the same files; no conflict unless a bot branch is open. +- Gate false positives: comparing to package version (source iii) would fail on Unsafe (6.1.2 vs 6.0.3.0) and Svg (3.4.8 vs 3.4.0.0). Redirects for assemblies with multiple Reference versions across projects (if any) need set-membership, not equality; not enumerated [U]. +- Gate vacuity: a detector reading zero redirects passes trivially; the examined-count guard and the negative control are required. +- Pester CI line floor is 80 in the workflow versus 85 in the rules: target 85. + +## Recommendation + +1. Remedy: option (a). Change the 11 stale Fizzler blocks to `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`. Do not remove blocks. +2. Gate: a new Pester detector over text, with an injected `DeployedVersionProvider` delegate fed in the real-repo test from csproj `Reference Include="Name, Version="` values (source ii). Assert zero findings across all 17 `app.config` files with an examined-count guard; include a negative control (fixture with a redirect to a version no Reference provides, mirroring #418 ExCSS/#953 Fizzler) and a positive control. Report unverifiable names separately after the planner measures how many exist. +3. Footprint: 11 `app.config` files; new `scripts/dependencies/BindingRedirectVerification.psm1`; new `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`; modified `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1`. Fallback if no production slot is available: modify `scripts/dependencies/ProjectConsistency.psm1` instead of adding the module. From f99c0c85e21a84f5b45523973fbde7ea3cb3f2b4 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:22:58 -0400 Subject: [PATCH 03/14] docs(953): record acceptance criteria and preparation reconciliation in issue --- .../issue.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md index b8a17ed3d..f11be21fc 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md @@ -71,6 +71,23 @@ The single Fizzler config already at `1.3.1.0` should be identified, since it ma Referred here from #418, which scoped both out explicitly: "Fizzler binding redirects" and "`System.Runtime.CompilerServices.Unsafe` redirects in any project other than `SVGControl.Test`". #418's `evidence/baseline/` artifacts and its research artifact carry the supporting assembly-metadata analysis. +## Preparation Reconciliation (2026-10-02, origin/main 59cbab04f) + +The record above was captured 2026-08-04. The state on main at preparation time differs in three respects; the Acceptance Criteria below are written against the current state. + +- The `System.Runtime.CompilerServices.Unsafe` outlier is already fixed. All 17 `app.config` files redirect it to `6.0.3.0`, including `SVGControl/app.config` (changed by issue 929, PR 949). No edit is planned for it. +- Fizzler: 13 configs carry a redirect. `SVGControl/app.config` (fixed by issue 929) and `UtilitiesCS/app.config` read `1.3.1.0`; eleven still read `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"`. The deployed `Fizzler.dll` is `1.3.1.0` and the only csproj `Reference` for it is `1.3.1.0`. +- A gate asserting that every `newVersion` in the repository matches a deployed version is not satisfiable today. A text-only measurement (all 17 configs, 1176 redirect entries, 18 csproj files) found 148 entries in 16 distinct assembly and version pairs whose `newVersion` is below the only csproj `Reference` version (Fizzler is one of the 16), and 29 entries for 3 assemblies with no csproj `Reference`. The gate in this fix therefore ratchets: it fails on any mismatch outside a recorded known-debt set and fails when a known-debt entry stops mismatching. Correcting the other 15 pairs is out of scope for this fix and is reported as a follow-up. + +## Acceptance Criteria + +- [ ] AC1: All 13 `app.config` files that carry a Fizzler `bindingRedirect` read `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`; the 11 stale files (QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test) each change on that one line only, with the UTF-8 BOM and CRLF line endings preserved. +- [ ] AC2: All 17 `app.config` files still redirect `System.Runtime.CompilerServices.Unsafe` to `6.0.3.0` (already delivered by issue 929; verified, not edited). +- [ ] AC3: A new Pester-tested detector reports a `bindingRedirect` whose `newVersion` equals no csproj `Reference` version for that assembly name, with a negative control (fixture redirect to a version no `Reference` provides yields one finding), a positive control (matching fixture yields none), and an examined-entry count that guards against a vacuous zero-finding pass. +- [ ] AC4: A repository-level Pester test runs the detector over every `app.config` against every csproj `Reference` and asserts that the findings equal exactly the recorded known-debt set (15 assembly and version pairs, none of them Fizzler or Unsafe) and that the unverifiable names equal exactly the recorded set of 3; a regression of any Fizzler redirect, a new mismatch, or a stale known-debt entry fails the test. +- [ ] AC5: The Fizzler regression test fails before the 11 config edits and passes after them (fail-before evidence recorded). +- [ ] AC6: PoshQC format, analyze, and test report no errors, and Pester line coverage for the new module is at least 85 percent. + ## Next Step - [ ] Promote to GitHub issue (bug-report template) From c67b709b1d81304d53794a6637f693ec9a20b086 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:49:33 -0400 Subject: [PATCH 04/14] docs(953): add atomic plan for Fizzler redirect sweep and redirect ratchet gate --- .../issue.md | 2 +- .../plan.2026-10-02T00-16.md | 465 +++++++++++++++++- 2 files changed, 439 insertions(+), 28 deletions(-) diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md index f11be21fc..a6b9b7b0d 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md @@ -86,7 +86,7 @@ The record above was captured 2026-08-04. The state on main at preparation time - [ ] AC3: A new Pester-tested detector reports a `bindingRedirect` whose `newVersion` equals no csproj `Reference` version for that assembly name, with a negative control (fixture redirect to a version no `Reference` provides yields one finding), a positive control (matching fixture yields none), and an examined-entry count that guards against a vacuous zero-finding pass. - [ ] AC4: A repository-level Pester test runs the detector over every `app.config` against every csproj `Reference` and asserts that the findings equal exactly the recorded known-debt set (15 assembly and version pairs, none of them Fizzler or Unsafe) and that the unverifiable names equal exactly the recorded set of 3; a regression of any Fizzler redirect, a new mismatch, or a stale known-debt entry fails the test. - [ ] AC5: The Fizzler regression test fails before the 11 config edits and passes after them (fail-before evidence recorded). -- [ ] AC6: PoshQC format, analyze, and test report no errors, and Pester line coverage for the new module is at least 85 percent. +- [ ] AC6: PoshQC format, analyze, and test report no errors, and every exported function of the new module is exercised by at least one Pester test for its positive, negative, and edge paths. The Pester line-coverage figure for the new module is produced by the CI Pester job (workflow floor 80 percent, repository rule 85 percent) and is not measured locally. ## Next Step diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index fc42ea59e..10cbb1db6 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -3,42 +3,453 @@ - **Issue:** #953 - **Parent (optional):** none - **Owner:** drmoisan -- **Last Updated:** 2026-10-02T00-16 -- **Status:** Draft -- **Version:** 0.1 +- **Branch:** bug/stale-fizzler-and-unsafe-binding-redirects-953 +- **Last Updated:** 2026-10-02T01-30 +- **Status:** Draft (awaiting validator and executor preflight) +- **Version:** 1.0 +- **Work Mode:** minor-audit (issue.md line 9) +- **Task Count:** 47 (Phase 0: 13, Phase 1: 17, Phase 2: 17), counted mechanically over lines matching the task pattern -**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. +**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. For this plan the coverage figure is read by the CI Pester job and not locally (Decision D8); the reduced-audit handoff must carry that deferral explicitly, and a handoff that omits it is INCOMPLETE. **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. +## 1. Objective and scope -**Phase 0 — Context & Inputs** -- [ ] [P0-T1] Link approved spec: -- [ ] [P0-T2] Record branch/commit baseline: -- [ ] [P0-T3] List required environment/fixtures/data: +Sweep the eleven stale Fizzler `bindingRedirect` entries to `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`, add a Pester-tested detector that reports a `bindingRedirect` whose `newVersion` equals no csproj `Reference` version for its assembly name, and add a repository-level ratchet test that pins the remaining known-debt set so the next drift fails the suite. The `System.Runtime.CompilerServices.Unsafe` half of the issue is already delivered (issue 929, PR 949) and is verified, not edited. -**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 +Requirements source: `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md`, section `## Acceptance Criteria`, items AC1 to AC6 (lines 84 to 89). There is no spec.md or user-story.md and none is created; the minor-audit mode fails closed if either appears (Phase 0 task P0-T3). -**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//#953-.py` only if no clear home exists) -- [ ] [P2-T2] [expect-fail] Run the regression to confirm it fails and captures the repro +This plan is executed in a later run by `atomic-executor`. The planner executed nothing. Every repository-relative path below is relative to the execution worktree root, written in evidence as ``; absolute host paths are never transcribed into evidence. -**Phase 3 — Minimal Fix** -- [ ] [P3-T1] Apply the smallest change needed to make the regression test pass; avoid opportunistic refactors +## 2. Inputs read by the planner (2026-10-02) -**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 +CLAUDE.md; `.claude/rules/powershell.md`; `.claude/rules/plan-acceptance-gates.md`; `.claude/rules/tonality.md`; `.claude/skills/atomic-plan-contract/SKILL.md`; `.claude/skills/evidence-and-timestamp-conventions/SKILL.md`; `.claude/skills/acceptance-criteria-tracking/SKILL.md`; the feature issue.md and research artifact `research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md`; `scripts/dependencies/PackageGraph.psm1`; `scripts/dependencies/ProjectConsistency.psm1`; `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1`; `tests/scripts/dependencies/PackageGraph.Tests.ps1`; `.github/workflows/_pester.yml`; `.gitattributes`; `.gitignore`; `.claude/hooks/enforce-powershell-batch-budget.ps1`; the issue 929 evidence artifacts that recorded the PoshQC tools' success-case output and a redirect edit of the same class (`docs/features/active/2026-09-28-package-manifest-consistency-residuals-929/evidence/`). -**Phase 5 — Documentation & Status** -- [ ] [P5-T1] Update spec/issue with outcomes, decisions, and any deviations from scope +## 3. Design decisions (made by the orchestrator; recorded, not reopened) -**Phase 6 — PR & Handoff** -- [ ] [P6-T1] Prepare PR notes (summary, risks, validation performed, links to tests) and request review +- **D1 — Remedy is a sweep, not a removal.** The eleven stale blocks become `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`, the shape the two correct files (SVGControl, UtilitiesCS) already carry. No Fizzler block is removed. The two correct Fizzler files and all seventeen Unsafe redirects (every one at 6.0.3.0) are not edited. +- **D2 — Edit mechanism: the Edit tool with a two-line old_string.** The string `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` occurs 17 times across the 11 files: once on the Fizzler block in every one of them and once more on the System.ClientModel block in six of them (QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel). A single-line replacement is therefore wrong in six files and not unique for the Edit tool. The edit uses the `assemblyIdentity name="Fizzler"` line plus the following `bindingRedirect` line as one two-line old_string, which is unique in every file. `Invoke-BindingRedirectReconciliation` (ProjectConsistency.psm1 lines 271 to 375) was considered and rejected for the write: it is pure over text, so applying it requires a PowerShell read and write round trip in which `ReadAllText` discards the UTF-8 BOM and the write must re-add it with an explicit encoding, adding an encoding step whose correctness would itself need a gate, and a PowerShell-tool file write bypasses the PreToolUse hook chain the Edit tool passes through. If the Edit tool reports that the old_string was not found in a file, the executor records the tool message and stops (STOP: EDIT-MISMATCH); it does not rewrite the file by any other mechanism. +- **D3 — Encoding gates are worktree observations, not only git diffs.** `.gitattributes` line 4 sets `* text=auto`, so git normalises line terminators when it diffs the working tree; a rewrite that converted CRLF to LF could be invisible to `git diff`. Line-ending preservation is therefore observed with `git ls-files --eol`, whose `w/` column reports the working-tree file's own terminators (`w/crlf` expected; `w/lf` or `w/mixed` is a failure). BOM preservation is observed two ways: `git diff --numstat -- ` reading exactly `1 1 ` (a lost BOM alters line 1 and would read `2 2`), and a byte read of the first three bytes (CMD-BOM, expected `239,187,191`), compared before and after the edit. ripgrep strips a UTF-8 BOM before matching, so the Grep tool cannot observe it; the planner's zero-match Grep for the BOM bytes is inconclusive by construction and the directive's BOM claim is confirmed at P0-T7 by CMD-BOM. +- **D4 — Gate design: new module `scripts/dependencies/BindingRedirectVerification.psm1`.** It imports `PackageGraph.psm1` (parser) and exports two pure functions: `ConvertTo-ReferenceVersionMap` (csproj texts to a name-to-versions map) and `Find-StaleBindingRedirect` (app.config text plus an injected `-DeployedVersionProvider` scriptblock to findings, an unverifiable-name list and an examined-entry count). `ConsistencyVerifier.psm1` is at 499 lines and cannot host it; `ProjectConsistency.psm1` (381 lines) documents a reconciliation-only ownership split in its header (lines 6 to 12). The provider seam is the injectable-delegate seam the PowerShell rule permits when a wrapper is insufficient; it lets every unit test run on in-memory fixtures with no temporary file and no `$TestDrive`. +- **D5 — Source of truth is the global set of csproj `Reference Include="Name, Version=..."` values.** Membership is by assembly name across every csproj, not per project: 535 of the 1176 redirect entries are for assemblies the config's own csproj does not reference, so a per-project check would report hundreds of false findings. packages.config versions are not used (Unsafe package 6.1.2 versus assembly 6.0.3.0; Svg 3.4.8 versus 3.4.0.0). Assembly metadata under packages/ is not usable because the CI Pester job (`.github/workflows/_pester.yml`) performs no NuGet restore and the worktree carries no packages tree. A name with no csproj Reference anywhere is unverifiable, listed separately, and not a finding. +- **D6 — Tests live in one new file, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`.** `RepositoryTreeConsistency.Tests.ps1` is not modified: the session PowerShell file budget is shared and believed full, and the new file holds both the unit tests and the two repository-level tests. The existing SVGControl-only redirect test (RepositoryTreeConsistency.Tests.ps1 lines 92 to 110) keeps passing after the sweep because SVGControl is not edited. +- **D7 — Ratchet on (assembly name, newVersion) pairs; re-measure in Phase 0.** The repository-level test asserts that the sorted distinct set of `AssemblyName|NewVersion` findings equals exactly the fifteen recorded known-debt pairs (section 7) and that the sorted distinct unverifiable names equal exactly the recorded three. It fails on a new mismatch, on a stale known-debt entry and on any Fizzler regression. It does not key on per-config counts. P0-T8 re-measures every row by Grep; if any row, total or set differs from section 7 the executor records `KNOWN-DEBT-DRIFT:` with the differing rows and stops, because the table would then describe a tree the orchestrator has not measured; the executor never edits the table or the test literal on its own authority. Correcting the fifteen pairs is out of scope; P2-T16 records the follow-up for the coordinator to promote. The examined-count guard compares the detector's total to an independent count of ` -- ...`) paired with `git status --porcelain --untracked-files=all`; neither can pass vacuously while the changes are uncommitted, and the pairing covers the new (untracked) files that a name-listing diff cannot see. +- **D11 — No C# toolchain.** No `*.cs`, `*.csproj`, `*.sln`, `*.props`, `*.targets` or packages.config file is touched. `.csharpierignore` excludes `**/app.config`. CSharpier, msbuild analyzer and nullable builds and the MSTest coverage run do not apply; P2-T7 proves the scope by pathspec. +- **D12 — Evidence locations.** Every artifact is written under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence//` with kinds baseline, regression-testing, qa-gates and other. No `artifacts/` path is an evidence location; `artifacts/pester/` is a read source only (gitignored at `.gitignore` line 57) and `artifacts/orchestration/orchestrator-state.json` is untracked and outside the footprint. -**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 +## 4. Tree facts verified by the planner (re-derived 2026-10-02 in this worktree) + +| Fact | Evidence | Used by | +|---|---|---| +| 17 app.config files at root level; 1176 `assemblyIdentity name=` entries and 1176 `' with 2 selected scan folder(s)."}`; analyze `...PoshQC analyze against '' with 2 selected scan folder(s).`; test `...PoshQC test against '' with 1 selected scan folder(s).`; a failing test run returns `{"ok": false, ..., "summary": "Command exited with code 4."}`; the JUnit document carries one `testsuite` per file (absolute-path `name`, `tests`, `failures`, `skipped`) and one `testcase` per It (`name` equal to Describe dot It, `status`, a `failure` child with `message`) | 929 evidence p0-t13, p0-t14, p0-t15, p1-t2 | CMD definitions | +| Issue 929 recorded a two-line Edit-tool change to SVGControl/app.config as `git diff --numstat` `2 2` with indentation unchanged | 929 evidence p1-t5 | D3 (one line edits read `1 1`) | +| `enforce-powershell-batch-budget.ps1` lines 8-15 (caps 3 and 3, state under `.claude/state/powershell-batch-budget..json`), 37-40 (override variables), 42-45 (deny) | Read | D9, P0-T13 | +| issue.md: `- Work Mode: minor-audit` at line 9; `## Acceptance Criteria` at 82; AC1 to AC6 at 84-89, all `- [ ]`; AC6 contains `is not measured locally` | Read | P0-T3, P2-T9 to P2-T14 | + +## 5. Write Set (exhaustive) + +1. `QuickFiler/app.config` (lines 50-51) +2. `QuickFiler.Test/app.config` (46-47) +3. `SVGControl.Test/app.config` (18-19) +4. `Tags/app.config` (46-47) +5. `TaskMaster/app.config` (50-51) +6. `TaskTree/app.config` (46-47) +7. `TaskVisualization/app.config` (46-47) +8. `TaskVisualization.Test/app.config` (46-47) +9. `ToDoModel/app.config` (51-52) +10. `ToDoModel.Test/app.config` (46-47) +11. `UtilitiesCS.Test/app.config` (46-47) +12. `scripts/dependencies/BindingRedirectVerification.psm1` (new) +13. `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` (new) +14. `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/` (issue.md AC check-offs, this plan's checkboxes, `evidence/`) + +Nothing else is written. `artifacts/pester/*` is written by the MCP test tool and is gitignored; `artifacts/orchestration/orchestrator-state.json` is untracked and outside the footprint. `.claude/agent-memory/**` is tracked and written by agents during execution; every git gate in this plan subtracts that prefix (Convention C6). + +## 6. Conventions and command definitions + +- **C1 — Artifact fields.** Every command-bearing artifact carries `Timestamp: `, `Command: `, `EXIT_CODE: `, `Output Summary:` (1 to 20 lines). A task expected to observe a red state carries `ExpectedExitCode: 1`. One artifact carries one `EXIT_CODE:` row; additional exit values are named `Output Summary:` lines. +- **C2 — Artifact naming.** `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence//-..md`, for example `evidence/baseline/p0-t4-fizzler-census.2026-10-05T09-12.md`. Loop iterations add `.iter` before the timestamp. +- **C3 — MCP exit derivation.** For an MCP PoshQC call, `EXIT_CODE:` is 0 when the payload's `ok` is true (and, for the test tool, when the JUnit root `failures` is 0) and 1 otherwise; the payload is recorded verbatim with the worktree root replaced by ``. The tool's own process exit is not observable and is not claimed. +- **C4 — Host paths.** No evidence line carries an absolute host path, machine name or account name; the worktree prefix is replaced by ``. +- **C5 — Tool routes.** Read-only tree observations use the Read, Grep and Glob tools or `git`. Grep patterns written in this plan are ripgrep patterns run through the Grep tool with the stated glob. Two PowerShell one-liners (CMD-BOM, CMD-JUNIT-DELETE) contain no double quote and no `$`; they are run with the current directory at the worktree root. No other PowerShell command is issued. +- **C6 — Git gate scoping.** Every `git diff` is anchored at `` (P0-T2). Every footprint evaluation removes paths under `.claude/agent-memory/` and paths listed in the P0-T2 `INHERITED:` capture before comparing against the Write Set. Gitignored paths (`artifacts/`, `coverage/`) never appear in porcelain and need no subtraction. +- **C7 — Stop rule.** Where a task says STOP, the executor writes the task's artifact with the observed values, leaves the plan checkbox unchecked, and ends the run with a report to the orchestrator. No stop is a pass. +- **C8 — Check-off protocol.** Plan checkboxes are flipped only after the task's artifact exists with every C1 field. issue.md check-offs happen only in P2-T9 to P2-T14, one criterion per task, changing `- [ ]` to `- [x]` and nothing else on the line. + +**CMD-POSHQC-FORMAT** — MCP `mcp__drm-copilot__run_poshqc_format` with `workspace_root` `` and `scan_folders` `["scripts/dependencies","tests/scripts/dependencies"]`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC format against '' with 2 selected scan folder(s).` The tool reports no file list and exits the same whether or not it rewrote a file, so its observation is CMD-HASHSET taken immediately before and immediately after the call; identical hash sets mean no rewrite. + +**CMD-HASHSET** — `git hash-object ` for every `.ps1` and `.psm1` file under `scripts/dependencies/` and `tests/scripts/dependencies/` (the files are enumerated with the Glob tool at run time; 13 at planning time, 15 once the two new files exist). `git hash-object` reads the working-tree file whether or not it is tracked, so an untracked new file is observed. The set is recorded as `HASH ` lines sorted by path. + +**CMD-POSHQC-ANALYZE** — MCP `mcp__drm-copilot__run_poshqc_analyze` with the same `workspace_root` and `scan_folders`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC analyze against '' with 2 selected scan folder(s).` The payload carries no count, file or rule; `ok` true is the lint result and is recorded with the line `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. `ok` false is a failed step. + +**CMD-JUNIT-DELETE** — `pwsh -NoProfile -Command 'Remove-Item -LiteralPath artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue'` followed by a Glob for `artifacts/pester/pester-junit.xml` that must return nothing. Run immediately before every CMD-POSHQC-TEST so the document read afterwards was written by that run. The pwsh payload contains no double quote. + +**CMD-POSHQC-TEST** — MCP `mcp__drm-copilot__run_poshqc_test` with `workspace_root` `` and `scan_folders` `["tests/scripts/dependencies"]`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC test against '' with 1 selected scan folder(s).`; a failing run observed on this repository returned `ok` false with summary `Command exited with code 4.` The tool writes `artifacts/pester/pester-junit.xml` (and two coverage documents that instrument none of `scripts/dependencies` and are never read). Every test task records the payload verbatim (C3, C4), then runs CMD-JUNIT-READ, and carries the literal line `COVERAGE-MEASUREMENT: deferred to the CI Pester job (_pester.yml); no local figure`. + +**CMD-JUNIT-READ** — Over `artifacts/pester/pester-junit.xml`, which must exist (Glob) after the run: (a) Grep pattern ` failures= errors= disabled=` from that line's attributes; (b) Grep pattern ` tests= failures= skipped=` line per match, the leaf taken after the last path separator so no host path is transcribed; (c) Grep pattern `status="Failed"` with `-A 2` → one `JUNIT-NOTPASSED ` line per matched testcase and a `JUNIT-MESSAGE ` line, with any worktree prefix replaced per C4; when (c) returns no match the artifact states `JUNIT-NOTPASSED: none`. Expected suite leaf names at planning time: AnalyzerItemRepair.Tests.ps1, ConsistencyVerifier.Tests.ps1, DependabotConfig.Tests.ps1, PackageCompatibility.Tests.ps1, PackageGraph.Tests.ps1, ProjectConsistency.Tests.ps1, Repair-PackageManifestConsistency.Tests.ps1, RepositoryTreeConsistency.Tests.ps1 and, from P1-T2, BindingRedirectVerification.Tests.ps1. Suite counts are recorded as observed at P0-T12 and compared against that record later; this plan pins no pre-existing suite's count. + +**CMD-EOL ``** — `git ls-files --eol -- `. Output is one line of the form `i/ w/ attr/ `. The gate reads the second field, which describes the working-tree file's terminators: `w/crlf` passes; `w/lf`, `w/mixed`, `w/none` or `w/-text` fails. The first field is recorded and not gated. + +**CMD-BOM ``** — `pwsh -NoProfile -Command '(Get-Content -LiteralPath -AsByteStream -TotalCount 3) -join [char]44'` with the current directory at the worktree root. A UTF-8 BOM prints `239,187,191`. The payload contains no double quote; `[char]44` is the comma separator. Recorded as `BOM-BYTES =`. + +**CMD-NUMSTAT ``** — `git diff --numstat -- `. For a one-line replacement the output is exactly `1 1 ` (tab separated). Paired in the same task with `git diff -- `, whose content lines (those beginning `-` or `+` after the `---`/`+++` header pair) must be exactly one removed line and one added line. + +**CMD-LINECOUNT ``** — Grep tool, pattern `^`, output_mode count, path ``; the returned count is the file's line count. Used for every line-count figure in this plan so figures are comparable. + +**CMD-FOOTPRINT** — `git diff --name-only -- .` together with `git status --porcelain --untracked-files=all`, both recorded verbatim (C4). The evaluated set is the union of the two captures' paths minus the P0-T2 `INHERITED:` list minus every path under `.claude/agent-memory/` (C6). + +**EDIT-FIZZLER `` `` ``** — Edit tool on `` with old_string equal to the two lines (identity line, then redirect line, each with its 8-space indent): + +```text + + +``` + +and new_string equal to the same two lines with the second replaced by: + +```text + +``` + +Acceptance for every EDIT-FIZZLER task (all six must hold): (1) CMD-NUMSTAT prints exactly `1 1 `; (2) `git diff -- ` shows exactly one `-` content line, whose text ignoring a trailing carriage return is ` `, and exactly one `+` content line, whose text ignoring a trailing carriage return is ` `; (3) CMD-EOL prints `w/crlf` in the second field; (4) CMD-BOM prints a value equal to the `BOM-BYTES` value P0-T7 recorded for the same path (expected `239,187,191`); (5) Grep `name="Fizzler"` with `-A 1` on `` shows the redirect line now reading `1.3.1.0` in both attributes; (6) Grep `name="System.ClientModel"` with `-A 1` on `` shows the System.ClientModel redirect line unchanged from P0-T5 (1.3.0.0 in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; 1.16.0.0 in the five test configs). The task artifact goes to `evidence/qa-gates/` with `Command:` naming the Edit and the four observation commands and `EXIT_CODE: 0`. If the Edit tool reports no match, STOP: EDIT-MISMATCH (D2). + +## 7. Known-debt set (orchestrator measurement 2026-10-02, re-derived by the planner 2026-10-02, re-measured by P0-T8) + +Findings expected after the Fizzler sweep: exactly these 15 pairs, 137 redirect entries in all (assembly | config newVersion | only csproj Reference version | configs carrying it): + +1. Azure.Core | 1.62.0.0 | 1.63.0.0 | 6 +2. Microsoft.Bcl.Memory | 10.0.0.7 | 10.0.0.12 | 10 +3. Microsoft.Bcl.Numerics | 10.0.0.5 | 10.0.0.12 | 12 +4. Microsoft.Extensions.Diagnostics.Abstractions | 10.0.0.5 | 10.0.0.12 | 6 +5. Microsoft.Identity.Client | 4.89.0.0 | 4.90.1.0 | 6 +6. Microsoft.Identity.Client.Extensions.Msal | 4.89.0.0 | 4.90.1.0 | 6 +7. Microsoft.IdentityModel.Abstractions | 8.22.0.0 | 8.23.0.0 | 6 +8. Microsoft.IdentityModel.JsonWebTokens | 8.22.0.0 | 8.23.0.0 | 7 +9. Microsoft.IdentityModel.Logging | 8.22.0.0 | 8.23.0.0 | 7 +10. Microsoft.IdentityModel.Protocols | 8.22.0.0 | 8.23.0.0 | 13 +11. Microsoft.IdentityModel.Protocols.OpenIdConnect | 8.22.0.0 | 8.23.0.0 | 13 +12. Microsoft.IdentityModel.Tokens | 8.22.0.0 | 8.23.0.0 | 13 +13. Microsoft.IdentityModel.Validators | 8.22.0.0 | 8.23.0.0 | 13 +14. System.IdentityModel.Tokens.Jwt | 8.22.0.0 | 8.23.0.0 | 13 +15. System.ClientModel | 1.3.0.0 | 1.16.0.0 | 6 + +Unverifiable (no csproj `Reference Include="Name, Version=` anywhere): System.Linq.AsyncEnumerable (15 entries), Microsoft.IdentityModel.Clients.ActiveDirectory (13), netstandard (1) = 29 entries. Before the sweep the finding set additionally contains `Fizzler|1.3.0.0` (11 entries, 148 in all), which is why test 14 is red before Phase 1 and green after. + +Scope boundary: correcting these 15 pairs is out of scope for issue 953. P2-T16 records them as a follow-up note for the coordinator to promote through the potential-entry lifecycle; this plan creates no issue and no file outside the Write Set. + +## 8. Module specification (`scripts/dependencies/BindingRedirectVerification.psm1`) + +ASCII-only content (so `PSUseBOMForUnicodeEncodedFile` cannot fire), target 100 to 150 lines, hard ceiling 500. Comment-based help on both functions. Written as follows; the executor reproduces this content, adjusting only formatting that the PoshQC formatter requires. + +```powershell +<# +.SYNOPSIS + Detects application-configuration binding redirects whose newVersion names an assembly + version that no project file Reference declares. + +.DESCRIPTION + BindingRedirectVerification is a detection layer beside the dependency-consistency + tooling for issue #911: PackageGraph parses, ProjectConsistency reconciles and + ConsistencyVerifier detects manifest and project-file faults. This module carries the + one rule issue #953 adds. A bindingRedirect newVersion must equal a version that some + project file declares in a Reference Include for the same assembly name, because that is + the assembly version the build copies to the output directory. Package versions are not + consulted: a package version and its assembly version are different quantities. + + Both functions are pure over text. The deployed-version source is an injected + scriptblock, so the detector runs in memory with no file dependency; the repository-level + test supplies a provider built from every project file. + + Exported functions: + - ConvertTo-ReferenceVersionMap + - Find-StaleBindingRedirect + #> + +Set-StrictMode -Version Latest + +<# Imported without -Force deliberately, as ProjectConsistency.psm1 does: a nested + Import-Module -Force removes the module from the whole session before re-importing it, + which would strip the parser from a caller that had already imported it. #> +Import-Module (Join-Path $PSScriptRoot 'PackageGraph.psm1') + +function ConvertTo-ReferenceVersionMap { + <# + .SYNOPSIS + Builds a map from assembly name to the versions the supplied project files declare + in Reference Include attributes. + .DESCRIPTION + Only an Include of the form "Name, Version=X.Y.Z.W, ..." contributes. A Reference + without a Version is not evidence of a deployed version and is skipped. Versions are + unioned across every supplied text, so an assembly two projects reference at two + versions maps to both. Empty or whitespace project text is rejected by the parser. + .PARAMETER ProjectText + The project-file texts. An empty collection yields an empty map. + #> + [CmdletBinding()] + [OutputType([hashtable])] + param( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [string[]]$ProjectText + ) + + $versions = @{} + foreach ($text in $ProjectText) { + $reference = @(ConvertFrom-ProjectFileText -Text $text | Where-Object { $_.Kind -eq 'Reference' }) + foreach ($record in $reference) { + $match = [regex]::Match($record.Value, '^\s*(?[^,]+?)\s*,\s*Version=(?[^,\s]+)') + if (-not $match.Success) { continue } + $name = $match.Groups['name'].Value + if (-not $versions.ContainsKey($name)) { + $versions[$name] = [System.Collections.Generic.List[string]]::new() + } + $value = $match.Groups['version'].Value + if (-not $versions[$name].Contains($value)) { $versions[$name].Add($value) } + } + } + + $map = @{} + foreach ($key in $versions.Keys) { $map[$key] = [string[]]$versions[$key].ToArray() } + return $map +} + +function Find-StaleBindingRedirect { + <# + .SYNOPSIS + Reports every bindingRedirect whose newVersion equals no deployed version of its + assembly, with the examined-entry count and the unverifiable assembly names. + .DESCRIPTION + A dependentAssembly block with no bindingRedirect is not examined. An assembly for + which the provider returns no version is listed as unverifiable and is not a finding, + so a redirect for a transitively deployed assembly needs no allow list. Only + newVersion is compared; the oldVersion range is a request filter, not a deployment + claim. Empty or whitespace text examines zero entries. Text that is not an + application configuration document is rejected by the parser. + .PARAMETER AppConfigText + The application configuration text. + .PARAMETER DeployedVersionProvider + A delegate taking an assembly name and returning its deployed version strings, or + nothing when the name is unknown. + #> + [CmdletBinding()] + [OutputType([pscustomobject])] + param( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string]$AppConfigText, + + [Parameter(Mandatory = $true)] + [scriptblock]$DeployedVersionProvider + ) + + $finding = [System.Collections.Generic.List[pscustomobject]]::new() + $unverifiable = [System.Collections.Generic.List[string]]::new() + $examined = 0 + + if (-not [string]::IsNullOrWhiteSpace($AppConfigText)) { + foreach ($record in @(ConvertFrom-AppConfigText -Text $AppConfigText)) { + if ([string]::IsNullOrEmpty($record.NewVersion)) { continue } + $examined++ + $deployed = @(& $DeployedVersionProvider $record.Name | + Where-Object { -not [string]::IsNullOrEmpty([string]$_) } | + ForEach-Object { [string]$_ }) + if ($deployed.Count -eq 0) { + if (-not $unverifiable.Contains($record.Name)) { $unverifiable.Add($record.Name) } + continue + } + if ($deployed -contains $record.NewVersion) { continue } + $finding.Add([pscustomobject]@{ + PSTypeName = 'BindingRedirectVerification.StaleRedirect' + AssemblyName = $record.Name + NewVersion = $record.NewVersion + DeployedVersions = [string[]]$deployed + }) + } + } + + return [pscustomobject]@{ + PSTypeName = 'BindingRedirectVerification.DetectionResult' + Finding = $finding.ToArray() + Unverifiable = $unverifiable.ToArray() + ExaminedCount = $examined + } +} + +Export-ModuleMember -Function @( + 'ConvertTo-ReferenceVersionMap', + 'Find-StaleBindingRedirect' +) +``` + +## 9. Test specification (`tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`) + +ASCII-only, under 500 lines, `Set-StrictMode -Version Latest` at line 1, Arrange-Act-Assert comments in every It, one behaviour per It. `BeforeAll` resolves `$script:RepoRoot` from `$PSScriptRoot` exactly as RepositoryTreeConsistency.Tests.ps1 line 4 does, imports `scripts/dependencies/PackageGraph.psm1` with `-Force` and then `scripts/dependencies/BindingRedirectVerification.psm1` with `-Force` (the parser's functions are needed in test scope by test 13 and are not re-exported by the new module), defines a `ConvertTo-CrLf` helper as PackageGraph.Tests.ps1 lines 10-13 do, and holds every fixture as an in-memory string: a configuration wrapper (`...`) around dependentAssembly blocks for Fizzler at 1.3.0.0 (`oldVersion="0.0.0.0-1.3.0.0"`), Fizzler at 1.3.1.0, Fizzler at 1.3.1.0 with `oldVersion="0.0.0.0-9.9.9.9"`, System.Runtime.CompilerServices.Unsafe at 6.0.3.0, an assembly named Contoso.Unknown at 1.0.0.0, and a block with an assemblyIdentity (Contoso.NoRedirect) and no bindingRedirect; a provider scriptblock returning `@('1.3.1.0')` for Fizzler, `@('6.0.3.0')` for System.Runtime.CompilerServices.Unsafe and `@()` otherwise; a second provider returning `@('1.3.0.0','1.3.1.0')` for Fizzler; and two project-file fixtures in the shape of PackageGraph.Tests.ps1 lines 71-85 (ProjectA with `` and ``; ProjectB with ``). No `$TestDrive`, `New-Item`, `Set-Content`, `Out-File`, `Add-Content` or `New-TemporaryFile` appears anywhere in the file. + +The 14 It names, verbatim, with the assertion each carries: + +Describe 'Find-StaleBindingRedirect (in-memory fixtures)': + +1. `reports one finding when the Fizzler redirect names 1.3.0.0 and the provider deploys 1.3.1.0` — negative control: config with Fizzler 1.3.0.0 plus Unsafe 6.0.3.0; `Finding.Count` 1; the finding's `AssemblyName` is `Fizzler`, `NewVersion` is `1.3.0.0`, `DeployedVersions` is `@('1.3.1.0')`; `ExaminedCount` 2. +2. `reports no finding when the Fizzler redirect names the deployed 1.3.1.0` — positive control: `Finding.Count` 0; `ExaminedCount` 2; `Unverifiable.Count` 0. +3. `compares newVersion only and ignores the oldVersion range` — Fizzler block with `oldVersion="0.0.0.0-9.9.9.9" newVersion="1.3.1.0"`: `Finding.Count` 0. +4. `lists an assembly the provider knows nothing about as unverifiable and not as a finding` — Contoso.Unknown block: `Unverifiable` equals `@('Contoso.Unknown')`; `Finding.Count` 0; `ExaminedCount` 1. +5. `skips a dependentAssembly block that carries no bindingRedirect` — Contoso.NoRedirect block plus Fizzler 1.3.1.0: `ExaminedCount` 1; `Finding.Count` 0; `Unverifiable.Count` 0. +6. `counts one examined entry per bindingRedirect-bearing block` — three redirect-bearing blocks (Fizzler 1.3.1.0, Unsafe 6.0.3.0, Contoso.Unknown 1.0.0.0): `ExaminedCount` 3. +7. `examines zero entries and reports nothing for empty text` — `-AppConfigText ''`: `ExaminedCount` 0, `Finding.Count` 0, `Unverifiable.Count` 0, no throw. +8. `accepts a newVersion equal to any one of several deployed versions` — Fizzler 1.3.0.0 with the second provider: `Finding.Count` 0. +9. `throws when the text is not an application configuration document` — `-AppConfigText ''` inside a scriptblock piped to `Should -Throw`. + +Describe 'ConvertTo-ReferenceVersionMap (in-memory fixtures)': + +10. `maps a Reference Include with a Version to its assembly name` — ProjectA: the map contains key `Fizzler` with value `@('1.3.1.0')`. +11. `omits a Reference Include that declares no Version` — ProjectA: the map does not contain key `System.Xml`; key count 1. +12. `unions the versions of one assembly across several project texts` — ProjectA and ProjectB: `@($map['Fizzler'] | Sort-Object)` equals `@('1.3.0.0','1.3.1.0')`. + +Describe 'Repository binding redirects (issue 953)' (reads tracked files read-only through `[System.IO.File]::ReadAllText` on paths derived from `$script:RepoRoot`; writes nothing): + +13. `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` — Arrange: `$configPath` is every `app.config` directly under a root-level directory (`Get-ChildItem -LiteralPath $script:RepoRoot -Directory`, `Join-Path ... 'app.config'`, `Test-Path -LiteralPath`); `$configPath.Count | Should -BeGreaterThan 9`. Act: for each config, `ConvertFrom-AppConfigText` records whose `Name` is `Fizzler`, projected to `Config` (leaf directory name), `NewVersion`, `OldVersion`; `$stale` is those with `NewVersion -ne '1.3.1.0' -or OldVersion -ne '0.0.0.0-1.3.1.0'`. Assert: total Fizzler records `Should -Be 13 -Because 'thirteen configs carry a Fizzler redirect'`; `$stale.Count | Should -Be 0 -Because ('these configs redirect Fizzler to another version: ' + (($stale | ForEach-Object { $_.Config + '=' + $_.NewVersion }) -join '; '))`. This is the [expect-fail] regression test: before Phase 1 it fails with `Expected 0, because these configs redirect Fizzler to another version: ...=1.3.0.0; ..., but got 11.` naming the eleven directories; after P1-T14 it passes. +14. `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference` — Arrange: `$configPath` as in test 13; `$projectPath` is every `*.csproj` directly under a root-level directory; both counts `Should -BeGreaterThan 9`; `$map = ConvertTo-ReferenceVersionMap -ProjectText @($projectPath | ForEach-Object { [System.IO.File]::ReadAllText($_) })`; `$provider = { param($Name) $map[$Name] }.GetNewClosure()`; `$expectedDebt` is the 15 strings `Name|NewVersion` from section 7; `$expectedUnverifiable` is `@('Microsoft.IdentityModel.Clients.ActiveDirectory','System.Linq.AsyncEnumerable','netstandard')`. Act: for each config text, add `[regex]::Matches($text, '.md` with `Timestamp:`, `Policy Order:` (the numbered order above) and the explicit list of files read. Acceptance: the artifact exists and lists all eleven files. +- [ ] [P0-T2] Record the baseline git anchor for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: run `git fetch origin main` (record `FETCH-EXIT:`; a non-zero value is recorded and the run continues against the local `origin/main` ref with the line `FETCH-STALE: local origin/main used`), then `git merge-base HEAD origin/main` → `BASE_SHA:`, `git rev-parse HEAD` → `P0-START:`, `git cat-file -t `, `git status --porcelain --untracked-files=all` → `BASE-PORCELAIN:` (verbatim), `git diff --name-only -- .` → `INHERITED:` (verbatim; this is the set every later footprint evaluation subtracts, C6). Acceptance: BASE_SHA is 40 hexadecimal characters and `git cat-file -t` prints `commit`; no path in BASE-PORCELAIN or INHERITED ends in `.cs`, `.csproj`, `packages.config` or `app.config`, and none lies under `scripts/dependencies/` or `tests/scripts/dependencies/` (the Write Set is clean at start); the feature folder paths and `.claude/agent-memory/` paths are expected and recorded, never asserted empty. Artifact `evidence/baseline/p0-t2-base-anchor..md`. +- [ ] [P0-T3] Verify the acceptance-criteria source and mode as a baseline precondition by reading `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md`: line 9 reads `- Work Mode: minor-audit`; exactly one `## Acceptance Criteria` heading (Grep count of `^## Acceptance Criteria$` is 1); Grep count of `^- \[ \] AC[1-6]:` is 6 and of `^- \[x\] AC[1-6]:` is 0; the AC6 line contains the literal `is not measured locally`; Glob for `spec.md` and `user-story.md` in the feature folder returns nothing. Any failed condition is STOP: AC-SOURCE. Artifact `evidence/baseline/p0-t3-ac-precondition..md`. +- [ ] [P0-T4] Baseline Fizzler census over `*/app.config`: Grep pattern `name="Fizzler"` with `-A 1`, glob `*/app.config`, content mode. Acceptance: 13 blocks; the following line reads `newVersion="1.3.0.0"` for exactly these 11 files at these lines — QuickFiler/app.config 51, QuickFiler.Test/app.config 47, SVGControl.Test/app.config 19, Tags/app.config 47, TaskMaster/app.config 51, TaskTree/app.config 47, TaskVisualization/app.config 47, TaskVisualization.Test/app.config 47, ToDoModel/app.config 52, ToDoModel.Test/app.config 47, UtilitiesCS.Test/app.config 47 — and reads `newVersion="1.3.1.0"` for SVGControl/app.config 15 and UtilitiesCS/app.config 52. A different member set or line is STOP: TREE-DRIFT (the Write Set line citations would be wrong). Artifact `evidence/baseline/p0-t4-fizzler-census..md`. +- [ ] [P0-T5] Baseline shared-string census over `*/app.config`: Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → expected 17 occurrences across 11 files, 2 each in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel and 1 each in QuickFiler.Test, SVGControl.Test, TaskVisualization.Test, ToDoModel.Test, UtilitiesCS.Test; Grep pattern `name="System.ClientModel"` with `-A 1` → 17 blocks, the following line at 1.3.0.0 for exactly Tags 103, TaskVisualization 103, TaskTree 103, QuickFiler 103, TaskMaster 115, ToDoModel 108 and at 1.16.0.0 for the other 11. Acceptance: both expectations hold (otherwise STOP: TREE-DRIFT). The artifact records the per-file System.ClientModel values as `CLIENTMODEL =` lines for P1 comparison. Artifact `evidence/baseline/p0-t5-shared-string-census..md`. +- [ ] [P0-T6] Baseline Unsafe census over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1`, glob `*/app.config`. Acceptance: 17 blocks and every following line reads `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; the artifact lists the 17 files. Artifact `evidence/baseline/p0-t6-unsafe-census..md`. +- [ ] [P0-T7] Baseline encoding observation for the 11 Write Set configs (`QuickFiler/app.config` and the ten others of section 5): for each path run CMD-EOL (second field must read `w/crlf`), CMD-BOM (record `BOM-BYTES =`; expected `239,187,191`; a different value is recorded, not a stop, and becomes the preservation reference for Phase 1), and CMD-LINECOUNT paired with Grep pattern `\r$` count mode on the same file (the two counts must be equal; expected values QuickFiler 243, QuickFiler.Test 367, SVGControl.Test 227, Tags 239, TaskMaster 430, TaskTree 239, TaskVisualization 239, TaskVisualization.Test 363, ToDoModel 323, ToDoModel.Test 363, UtilitiesCS.Test 383). Acceptance: 11 `w/crlf` lines, 11 BOM-BYTES lines, 11 equal count pairs. Artifact `evidence/baseline/p0-t7-encoding-baseline..md`. +- [ ] [P0-T8] Baseline re-measurement of the known-debt table over `*/app.config` and `*/*.csproj`: (a) Grep count mode, glob `*/app.config`, pattern `assemblyIdentity name=` → 1176 total across 17 files; pattern `` → no match, and Grep glob `*/app.config`, pattern `name=""` count mode → 15, 13, 1 respectively. Acceptance: every figure equals section 7; any difference is recorded as `KNOWN-DEBT-DRIFT: ` and the run stops (D7). Completeness beyond the 18 names is proven by test 14 at P1-T15, not here. Artifact `evidence/baseline/p0-t8-known-debt-remeasure..md`. +- [ ] [P0-T9] Baseline line counts (CMD-LINECOUNT) for `scripts/dependencies/PackageGraph.psm1` (465), `scripts/dependencies/ProjectConsistency.psm1` (381), `scripts/dependencies/ConsistencyVerifier.psm1` (499), `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152), and Glob confirmation that `scripts/dependencies/BindingRedirectVerification.psm1` and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` do not yet exist. Acceptance: the four counts equal the figures in parentheses (a difference is recorded as `LINECOUNT-DRIFT:` and the run continues, because none of the four is edited) and both Globs return nothing (a hit is STOP: PREEXISTING-FILE). Artifact `evidence/baseline/p0-t9-linecount-baseline..md`. +- [ ] [P0-T10] Baseline PowerShell format step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, then CMD-POSHQC-FORMAT, then CMD-HASHSET again and `git status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies`. Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical and the porcelain over the two folders is empty (the Write Set is clean per P0-T2 and the new files do not exist yet). If a hash differs, the formatter rewrote pre-existing drift outside this item's change: record the paths as `FORMAT-DRIFT-REVERTED:`, restore each with `git checkout -- `, re-run CMD-HASHSET to confirm equality with the first set, and continue. `EXIT_CODE:` per C3. Artifact `evidence/baseline/p0-t10-poshqc-format..md`. +- [ ] [P0-T11] Baseline PowerShell analyze step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance: `ok` true with the 2-folder summary literal (the folders were analyzer-clean on 2026-09-30 per issue 929 evidence); `ok` false is STOP: ANALYZE-BASELINE-RED, because pre-existing analyzer debt in the scan scope is outside this plan and would make P2-T2 unsatisfiable. Record `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. Artifact `evidence/baseline/p0-t11-poshqc-analyze..md`. +- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. +- [ ] [P0-T13] Record the PowerShell budget baseline from `.claude/hooks/enforce-powershell-batch-budget.ps1`: quote lines 10-11 (caps), 14 (state file location) and 42-45 (deny behaviour); Glob `.claude/state/powershell-batch-budget.*.json` and record whether any state file exists (read-only; never opened for writing); restate the D9 stop rule in the artifact. Acceptance: the artifact quotes the three regions and carries the line `BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable`. Artifact `evidence/baseline/p0-t13-budget-baseline..md`. + +### Phase 1 — Implementation: detector module, regression tests, Fizzler sweep + +- [ ] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `-Force` count 0. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. +- [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `Import-Module .*PackageGraph\.psm1.* -Force` count 1 and `Import-Module .*BindingRedirectVerification\.psm1.* -Force` count 1, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. +- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12 is a defect in the module or tests: fix the new file concerned, record the correction, and re-run this task as `.iter2`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. +- [ ] [P1-T4] EDIT-FIZZLER `QuickFiler/app.config` lines 50-51; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t4-quickfiler-redirect..md`. +- [ ] [P1-T5] EDIT-FIZZLER `QuickFiler.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t5-quickfiler-test-redirect..md`. +- [ ] [P1-T6] EDIT-FIZZLER `SVGControl.Test/app.config` lines 18-19; System.ClientModel at 46-47 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t6-svgcontrol-test-redirect..md`. +- [ ] [P1-T7] EDIT-FIZZLER `Tags/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t7-tags-redirect..md`. +- [ ] [P1-T8] EDIT-FIZZLER `TaskMaster/app.config` lines 50-51; System.ClientModel at 114-115 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t8-taskmaster-redirect..md`. +- [ ] [P1-T9] EDIT-FIZZLER `TaskTree/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t9-tasktree-redirect..md`. +- [ ] [P1-T10] EDIT-FIZZLER `TaskVisualization/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t10-taskvisualization-redirect..md`. +- [ ] [P1-T11] EDIT-FIZZLER `TaskVisualization.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t11-taskvisualization-test-redirect..md`. +- [ ] [P1-T12] EDIT-FIZZLER `ToDoModel/app.config` lines 51-52; System.ClientModel at 107-108 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t12-todomodel-redirect..md`. +- [ ] [P1-T13] EDIT-FIZZLER `ToDoModel.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t13-todomodel-test-redirect..md`. +- [ ] [P1-T14] EDIT-FIZZLER `UtilitiesCS.Test/app.config` lines 46-47; System.ClientModel at 122-123 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t14-utilitiescs-test-redirect..md`. +- [ ] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. +- [ ] [P1-T16] Sweep verification over `*/app.config` (AC1 evidence): Grep pattern `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` glob `*/app.config` count mode → 13 occurrences across 13 files; Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → exactly 6 occurrences across exactly QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; Grep pattern `name="Fizzler"` with `-A 1` → 13 blocks all followed by `1.3.1.0` in both attributes; `git diff --name-only -- '*/app.config'` lists exactly the 11 Write Set config paths, paired with `git status --porcelain -- '*/app.config'` showing exactly 11 ` M` lines for the same paths. Acceptance: all four observations hold. Artifact `evidence/qa-gates/p1-t16-sweep-verification..md`. +- [ ] [P1-T17] Unsafe re-verification after the edits over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1` → 17 blocks all `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; `git diff -- '*/app.config'` contains no content line (beginning `-` or `+` after the header pairs) that contains `Unsafe`, and its 22 content lines are the 11 removed and 11 added Fizzler redirect lines. Acceptance: both hold. Artifact `evidence/qa-gates/p1-t17-unsafe-unchanged..md`. + +### Phase 2 — Final QC loop, footprint gate, acceptance check-off and reduced audit + +The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 returned `ok` false, or P2-T3 reported any failure, the executor fixes only Write Set PowerShell files (a rewrite of a file outside the Write Set is pre-existing drift: record it, restore with `git checkout -- `, continue) and restarts at P2-T1 as iteration N+1 with `.iter` artifacts. P2-T4 closes the loop only when one iteration shows no rewrite, `ok` true and zero failures together. No step may be recorded SKIPPED. + +- [ ] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (15 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. +- [ ] [P2-T2] Final QC analyze step (toolchain step 2) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal and the `GATE-SUBSTITUTION:` line. `ok` false: fix the new files only and restart at P2-T1. Type checking is not applicable to PowerShell (`.claude/rules/powershell.md` line 17) and is recorded as such in the artifact. Artifact `evidence/qa-gates/p2-t2-poshqc-analyze.iter..md`. +- [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. +- [ ] [P2-T4] Loop closure record for the QC toolchain under `evidence/qa-gates/`: list every iteration run (P2-T1, P2-T2, P2-T3 artifact names) and name the terminal iteration N on which P2-T1 recorded `REWRITE-COUNT: 0`, P2-T2 `ok` true and P2-T3 `failures=0` together. Acceptance: the three terminal-iteration artifacts exist (Glob) and carry those values. Artifact `evidence/qa-gates/p2-t4-loop-closure..md`. +- [ ] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10, 12), negative (11) and edge (14, through the repository-wide map) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. +- [ ] [P2-T6] File-size audit over `scripts/dependencies/` and `tests/scripts/dependencies/` after the terminal format pass: CMD-LINECOUNT for the two new files and the four P0-T9 neighbours. Acceptance: `BindingRedirectVerification.psm1` below 500 (record the value with `TARGET-BAND: 100-150` and whether it lies inside), `BindingRedirectVerification.Tests.ps1` below 500, and the four neighbour counts equal their P0-T9 values (they are not edited). Markdown under the feature folder is exempt from the 500-line cap per `.claude/rules/general-code-change.md`. Artifact `evidence/qa-gates/p2-t6-file-size-audit..md`. +- [ ] [P2-T7] No-C#-toolchain scope proof over the worktree: `git diff --name-only -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' 'packages.config'` prints nothing, paired with `git status --porcelain -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' 'packages.config'` printing nothing. Acceptance: both empty; the artifact states `CSHARP-TOOLCHAIN: not applicable; no C# source, project, solution or manifest file changed (D11)`. Artifact `evidence/qa-gates/p2-t7-no-csharp-scope..md`. +- [ ] [P2-T8] Footprint assertion over the worktree (CMD-FOOTPRINT): the evaluated set (C6) must equal the union of the 11 Write Set configs, `scripts/dependencies/BindingRedirectVerification.psm1`, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` and paths under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`. Acceptance: every evaluated path is in that union (any other path is STOP: FOOTPRINT, never reverted by this executor) and, as the positive control, all 13 named source paths are present in the evaluated set. Both captures are recorded verbatim per C4; the agent-memory subtraction is stated by composition, not by count. Artifact `evidence/qa-gates/p2-t8-footprint..md`. +- [ ] [P2-T9] Check off AC1 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 84, `- [ ] AC1:` to `- [x] AC1:`) citing P1-T4 to P1-T14 (one-line change, `w/crlf`, BOM preserved per file), P1-T16 (13 at 1.3.1.0, 11 changed paths) and P0-T7. Acceptance: Grep count of `^- \[x\] AC1:` in issue.md is 1 and the criterion text after the marker is unchanged. Artifact `evidence/qa-gates/p2-t9-ac1-checkoff..md`. +- [ ] [P2-T10] Check off AC2 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 85) citing P0-T6 and P1-T17. Acceptance: Grep count of `^- \[x\] AC2:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t10-ac2-checkoff..md`. +- [ ] [P2-T11] Check off AC3 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 86) citing P1-T1, P1-T2 and the P2-T3 terminal pass of tests 1 (negative control), 2 (positive control) and 6 (examined count). Acceptance: Grep count of `^- \[x\] AC3:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t11-ac3-checkoff..md`. +- [ ] [P2-T12] Check off AC4 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 87) citing P0-T8, P1-T15 and the P2-T3 terminal pass of test 14 (15 pairs, none Fizzler or Unsafe, 3 unverifiable names). Acceptance: Grep count of `^- \[x\] AC4:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t12-ac4-checkoff..md`. +- [ ] [P2-T13] Check off AC5 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 88) citing P1-T3 (fail-before, `but got 11`) and P1-T15 (pass-after). Acceptance: Grep count of `^- \[x\] AC5:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t13-ac5-checkoff..md`. +- [ ] [P2-T14] Check off AC6 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 89) citing P2-T1 to P2-T5 and quoting the AC6 literal `is not measured locally` as the authority for the CI coverage deferral (D8). Acceptance: Grep count of `^- \[x\] AC6:` is 1 and the text is unchanged; the artifact carries `COVERAGE-SOURCE: CI`. Artifact `evidence/qa-gates/p2-t14-ac6-checkoff..md`. +- [ ] [P2-T15] Acceptance-criteria status summary and plan reconciliation for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: Grep count of `^- \[x\] AC[1-6]:` in issue.md is 6 and of `^- \[ \] AC[1-6]:` is 0; the artifact carries the `### Acceptance Criteria Status` block (Source, Total AC items: 6, Checked off: 6, Remaining: 0). Acceptance: both counts hold. Artifact `evidence/qa-gates/p2-t15-ac-status..md`. +- [ ] [P2-T16] Follow-up note for the coordinator under `evidence/other/`: the 15 known-debt pairs of section 7 with their config counts and Reference versions, the 3 unverifiable names, the statement that correcting them is out of scope for issue 953, and the statement that this plan created no issue and no potential entry (the coordinator promotes). Acceptance: the artifact lists all 15 pairs and 3 names and carries `FOLLOW-UP: known-debt redirect correction, 15 pairs, 137 entries; promotion by the coordinator`. Artifact `evidence/other/p2-t16-known-debt-followup..md`. +- [ ] [P2-T17] Reduced-audit handoff index under `evidence/other/` for the QC and test evidence: `BASE_SHA:`, the Write Set, `COVERAGE-SOURCE: CI`, every `GATE-SUBSTITUTION:` line used, the budget outcome (no denial, or the denial record), the terminal loop iteration N, the final suite counts, and the path of every artifact written by P0-T1 through P2-T16 (bounded there; this artifact names itself only as the index). Acceptance: every artifact path named by P0-T1 through P2-T16 exists (Glob) and is listed; the index carries the literal `AUDIT-MODE: reduced (minor-audit)`. Artifact `evidence/other/p2-t17-reduced-audit-handoff..md`. + +## 11. Traceability + +| AC | Implementation | Tests | Evidence | +|---|---|---|---| +| AC1 | P1-T4 to P1-T14 (eleven EDIT-FIZZLER edits) | test 13; P1-T16 Grep counts | P0-T4, P0-T5, P0-T7, P1-T4 to P1-T14, P1-T16, P2-T9 | +| AC2 | none (verified, not edited) | RepositoryTreeConsistency.Tests.ps1 lines 92-110 (SVGControl Unsafe) | P0-T6, P1-T17, P2-T10 | +| AC3 | P1-T1 (`Find-StaleBindingRedirect`) | tests 1, 2, 6 (and 3 to 9) | P1-T1, P1-T2, P2-T3, P2-T11 | +| AC4 | P1-T1 (`ConvertTo-ReferenceVersionMap`, detector) | test 14 | P0-T8, P1-T15, P2-T3, P2-T12 | +| AC5 | P1-T3 before, P1-T15 after | test 13 | P1-T3, P1-T15, P2-T13 | +| AC6 | P2-T1 to P2-T5 | all 14 tests; P2-T5 map | P2-T1, P2-T2, P2-T3, P2-T4, P2-T5, P2-T14 | + +## 12. Residual risks + +1. **Edit tool line-ending handling.** The Edit tool was used on a CRLF, BOM-carrying app.config in issue 929 (P1-T5 evidence: numstat `2 2`, indentation unchanged). If a future tool version normalises terminators, CMD-EOL reads `w/lf` or `w/mixed` and the EDIT-FIZZLER task fails; the recovery is `git checkout -- ` and STOP: EDIT-MISMATCH, not a different write mechanism. +2. **Budget denial.** A spent production or test slot stops Phase 1 at P1-T1 or P1-T2 (D9). The fallback is not executed without the orchestrator's ruling. +3. **Dependabot churn.** A bot merge that bumps a csproj Reference without its redirect, or corrects a known-debt redirect, fails test 14 by design; P0-T8 detects such drift before any edit and stops the run. +4. **Coverage figure.** No local Pester line-coverage figure exists for `scripts/dependencies` through the MCP route. The CI Pester job supplies it on the pushed head; the handoff carries `COVERAGE-SOURCE: CI`. A reviewer who requires a local figure must raise it against AC6's wording, not against this plan. +5. **MCP test tool exit.** The tool's payload `ok` has been observed false with `Command exited with code 4.` on a red suite and true on a green one in this repository; its process exit code is not observable and is not claimed (C3). +6. **Hook-required preflight line.** The planner's output carries a `PREFLIGHT: REVISIONS REQUIRED` line because the SubagentStop hook bounds the internal-review record with it; the line records that executor preflight is outstanding and is not a discovered defect or a self-approval. + +## 13. Planner internal review + +PLANNER-INTERNAL-REVIEW: PASS +CITATION-TO-TREE: PASS +AC-TRACEABILITY: PASS +SCOPE-BOUNDARY: PASS +CITATION: QuickFiler/app.config | lines 50-51 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.3.0.0 +CITATION: QuickFiler.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.16.0.0 +CITATION: SVGControl.Test/app.config | lines 18-19 Fizzler 1.3.0.0; 46-47 System.ClientModel 1.16.0.0 +CITATION: Tags/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.3.0.0 +CITATION: TaskMaster/app.config | lines 50-51 Fizzler 1.3.0.0; 114-115 System.ClientModel 1.3.0.0 +CITATION: TaskTree/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.3.0.0 +CITATION: TaskVisualization/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.3.0.0 +CITATION: TaskVisualization.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.16.0.0 +CITATION: ToDoModel/app.config | lines 51-52 Fizzler 1.3.0.0; 107-108 System.ClientModel 1.3.0.0 +CITATION: ToDoModel.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.16.0.0 +CITATION: UtilitiesCS.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 122-123 System.ClientModel 1.16.0.0 +CITATION: SVGControl/app.config | lines 14-15 Fizzler 1.3.1.0 (not edited) +CITATION: UtilitiesCS/app.config | lines 51-52 Fizzler 1.3.1.0 (not edited) +CITATION: SVGControl/SVGControl.csproj | line 58 Reference Fizzler, Version=1.3.1.0 +CITATION: UtilitiesCS/UtilitiesCS.csproj | line 65 Reference Fizzler, Version=1.3.1.0; line 305 Reference System.ClientModel, Version=1.16.0.0 +CITATION: scripts/dependencies/PackageGraph.psm1 | ConvertFrom-ProjectFileText line 215; ConvertFrom-AppConfigText line 285; root check lines 304-305; Export-ModuleMember lines 457-465; 465 lines +CITATION: scripts/dependencies/ProjectConsistency.psm1 | header ownership lines 6-12; import without -Force lines 33-44; Invoke-BindingRedirectReconciliation lines 271-375; 381 lines +CITATION: scripts/dependencies/ConsistencyVerifier.psm1 | 499 lines; DetectionResult shape lines 46-54 +CITATION: tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 | RepoRoot line 4; import line 5; discovery guard line 74; examined guard lines 87-89; SVGControl redirect test lines 92-110; 152 lines +CITATION: tests/scripts/dependencies/PackageGraph.Tests.ps1 | ConvertTo-CrLf lines 10-13; app.config fixture lines 41-69; project fixture lines 71-85 +CITATION: .github/workflows/_pester.yml | Run.Path line 41; CodeCoverage.Path line 45; PESTER line 51; COVERAGE line 64; floor line 71 +CITATION: .claude/rules/powershell.md | toolchain lines 15-20; change budget lines 37-41; seams lines 43-52 +CITATION: .claude/hooks/enforce-powershell-batch-budget.ps1 | caps lines 10-11; state file line 14; override variables lines 37-40; deny lines 42-45 +CITATION: docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md | Work Mode line 9; Acceptance Criteria heading line 82; AC1-AC6 lines 84-89 +CITATION: docs/features/active/2026-09-28-package-manifest-consistency-residuals-929/evidence/qa-gates/p1-t5-redirects-fixed.2026-09-28T20-01.md | Edit-tool redirect change observed as numstat 2 2 +CITATION: docs/features/active/2026-09-28-package-manifest-consistency-residuals-929/evidence/baseline/p0-t15-poshqc-test-mcp.2026-09-28T20-01.md | run_poshqc_test payload literal and JUnit suite lines +AC-INVENTORY: AC1, AC2, AC3, AC4, AC5, AC6 +AC-MAPPING: AC1 | IMPLEMENTATION: P1-T4..P1-T14 EDIT-FIZZLER | TESTS: test 13 names 1.3.1.0 in every Fizzler binding redirect; P1-T16 greps | EVIDENCE: P0-T4, P0-T5, P0-T7, P1-T4..P1-T14, P1-T16, P2-T9 +AC-MAPPING: AC2 | IMPLEMENTATION: none, verified not edited | TESTS: RepositoryTreeConsistency.Tests.ps1 lines 92-110 | EVIDENCE: P0-T6, P1-T17, P2-T10 +AC-MAPPING: AC3 | IMPLEMENTATION: P1-T1 Find-StaleBindingRedirect | TESTS: tests 1, 2, 6 plus 3-9 | EVIDENCE: P1-T1, P1-T2, P2-T3, P2-T11 +AC-MAPPING: AC4 | IMPLEMENTATION: P1-T1 ConvertTo-ReferenceVersionMap and detector | TESTS: test 14 known-debt ratchet | EVIDENCE: P0-T8, P1-T15, P2-T3, P2-T12 +AC-MAPPING: AC5 | IMPLEMENTATION: P1-T3 fail-before and P1-T15 pass-after | TESTS: test 13 | EVIDENCE: P1-T3, P1-T15, P2-T13 +AC-MAPPING: AC6 | IMPLEMENTATION: P2-T1..P2-T5 QC loop and function-test map | TESTS: all 14 tests through run_poshqc_test | EVIDENCE: P2-T1, P2-T2, P2-T3, P2-T4, P2-T5, P2-T14 +UNRESOLVED-GAPS: NONE +PREFLIGHT: REVISIONS REQUIRED + +The PREFLIGHT line above is the hook-required terminator of the bounded record. It records that executor preflight has not yet run; it is not a discovered defect and not a self-approval. The orchestrator runs the validator and the atomic-executor preflight. From 2a2188f1cd5069f327570ce51b34eb66d7f118ba Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:18:24 -0400 Subject: [PATCH 05/14] docs(953): apply preflight round 1 revisions to the atomic plan --- .../plan.2026-10-02T00-16.md | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index 10cbb1db6..c093cf57d 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -4,9 +4,9 @@ - **Parent (optional):** none - **Owner:** drmoisan - **Branch:** bug/stale-fizzler-and-unsafe-binding-redirects-953 -- **Last Updated:** 2026-10-02T01-30 -- **Status:** Draft (awaiting validator and executor preflight) -- **Version:** 1.0 +- **Last Updated:** 2026-10-02T02-10 +- **Status:** Draft (awaiting validator and executor preflight; revision round 1 applied) +- **Version:** 1.1 - **Work Mode:** minor-audit (issue.md line 9) - **Task Count:** 47 (Phase 0: 13, Phase 1: 17, Phase 2: 17), counted mechanically over lines matching the task pattern @@ -30,7 +30,7 @@ CLAUDE.md; `.claude/rules/powershell.md`; `.claude/rules/plan-acceptance-gates.m - **D1 — Remedy is a sweep, not a removal.** The eleven stale blocks become `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`, the shape the two correct files (SVGControl, UtilitiesCS) already carry. No Fizzler block is removed. The two correct Fizzler files and all seventeen Unsafe redirects (every one at 6.0.3.0) are not edited. - **D2 — Edit mechanism: the Edit tool with a two-line old_string.** The string `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` occurs 17 times across the 11 files: once on the Fizzler block in every one of them and once more on the System.ClientModel block in six of them (QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel). A single-line replacement is therefore wrong in six files and not unique for the Edit tool. The edit uses the `assemblyIdentity name="Fizzler"` line plus the following `bindingRedirect` line as one two-line old_string, which is unique in every file. `Invoke-BindingRedirectReconciliation` (ProjectConsistency.psm1 lines 271 to 375) was considered and rejected for the write: it is pure over text, so applying it requires a PowerShell read and write round trip in which `ReadAllText` discards the UTF-8 BOM and the write must re-add it with an explicit encoding, adding an encoding step whose correctness would itself need a gate, and a PowerShell-tool file write bypasses the PreToolUse hook chain the Edit tool passes through. If the Edit tool reports that the old_string was not found in a file, the executor records the tool message and stops (STOP: EDIT-MISMATCH); it does not rewrite the file by any other mechanism. -- **D3 — Encoding gates are worktree observations, not only git diffs.** `.gitattributes` line 4 sets `* text=auto`, so git normalises line terminators when it diffs the working tree; a rewrite that converted CRLF to LF could be invisible to `git diff`. Line-ending preservation is therefore observed with `git ls-files --eol`, whose `w/` column reports the working-tree file's own terminators (`w/crlf` expected; `w/lf` or `w/mixed` is a failure). BOM preservation is observed two ways: `git diff --numstat -- ` reading exactly `1 1 ` (a lost BOM alters line 1 and would read `2 2`), and a byte read of the first three bytes (CMD-BOM, expected `239,187,191`), compared before and after the edit. ripgrep strips a UTF-8 BOM before matching, so the Grep tool cannot observe it; the planner's zero-match Grep for the BOM bytes is inconclusive by construction and the directive's BOM claim is confirmed at P0-T7 by CMD-BOM. +- **D3 — Encoding gates are worktree observations, not only git diffs.** `.gitattributes` line 4 sets `* text=auto`, so git normalises line terminators when it diffs the working tree; a rewrite that converted CRLF to LF could be invisible to `git diff`. Line-ending preservation is therefore observed with `git ls-files --eol`, whose `w/` field (the second whitespace-separated field of the output line; CMD-EOL) reports the working-tree file's own terminators (`w/crlf` expected; `w/lf` or `w/mixed` is a failure). BOM preservation is observed two ways: `git diff --numstat -- ` reading exactly `1 1 ` (a lost BOM alters line 1 and would read `2 2`), and a byte read of the first three bytes (CMD-BOM, expected `239,187,191`), compared before and after the edit. ripgrep strips a UTF-8 BOM before matching, so the Grep tool cannot observe it; the planner's zero-match Grep for the BOM bytes is inconclusive by construction and the directive's BOM claim is confirmed at P0-T7 by CMD-BOM. - **D4 — Gate design: new module `scripts/dependencies/BindingRedirectVerification.psm1`.** It imports `PackageGraph.psm1` (parser) and exports two pure functions: `ConvertTo-ReferenceVersionMap` (csproj texts to a name-to-versions map) and `Find-StaleBindingRedirect` (app.config text plus an injected `-DeployedVersionProvider` scriptblock to findings, an unverifiable-name list and an examined-entry count). `ConsistencyVerifier.psm1` is at 499 lines and cannot host it; `ProjectConsistency.psm1` (381 lines) documents a reconciliation-only ownership split in its header (lines 6 to 12). The provider seam is the injectable-delegate seam the PowerShell rule permits when a wrapper is insufficient; it lets every unit test run on in-memory fixtures with no temporary file and no `$TestDrive`. - **D5 — Source of truth is the global set of csproj `Reference Include="Name, Version=..."` values.** Membership is by assembly name across every csproj, not per project: 535 of the 1176 redirect entries are for assemblies the config's own csproj does not reference, so a per-project check would report hundreds of false findings. packages.config versions are not used (Unsafe package 6.1.2 versus assembly 6.0.3.0; Svg 3.4.8 versus 3.4.0.0). Assembly metadata under packages/ is not usable because the CI Pester job (`.github/workflows/_pester.yml`) performs no NuGet restore and the worktree carries no packages tree. A name with no csproj Reference anywhere is unverifiable, listed separately, and not a finding. - **D6 — Tests live in one new file, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`.** `RepositoryTreeConsistency.Tests.ps1` is not modified: the session PowerShell file budget is shared and believed full, and the new file holds both the unit tests and the two repository-level tests. The existing SVGControl-only redirect test (RepositoryTreeConsistency.Tests.ps1 lines 92 to 110) keeps passing after the sweep because SVGControl is not edited. @@ -48,7 +48,7 @@ CLAUDE.md; `.claude/rules/powershell.md`; `.claude/rules/plan-acceptance-gates.m | 17 app.config files at root level; 1176 `assemblyIdentity name=` entries and 1176 `/-..md`, for example `evidence/baseline/p0-t4-fizzler-census.2026-10-05T09-12.md`. Loop iterations add `.iter` before the timestamp. - **C3 — MCP exit derivation.** For an MCP PoshQC call, `EXIT_CODE:` is 0 when the payload's `ok` is true (and, for the test tool, when the JUnit root `failures` is 0) and 1 otherwise; the payload is recorded verbatim with the worktree root replaced by ``. The tool's own process exit is not observable and is not claimed. - **C4 — Host paths.** No evidence line carries an absolute host path, machine name or account name; the worktree prefix is replaced by ``. -- **C5 — Tool routes.** Read-only tree observations use the Read, Grep and Glob tools or `git`. Grep patterns written in this plan are ripgrep patterns run through the Grep tool with the stated glob. Two PowerShell one-liners (CMD-BOM, CMD-JUNIT-DELETE) contain no double quote and no `$`; they are run with the current directory at the worktree root. No other PowerShell command is issued. +- **C5 — Tool routes.** Read-only tree observations use the Read, Grep and Glob tools or `git`. Grep patterns written in this plan are ripgrep patterns run through the Grep tool with the stated glob. Two PowerShell one-liners (CMD-BOM, CMD-JUNIT-DELETE) contain no double quote and no `$`, and each names its file operand by absolute path. No other PowerShell command is issued. Every git command in this plan, including every one written in task text without the option, is issued as `git -C ...`, so pathspecs and `.` resolve against the worktree root whatever the executor's current directory; every Grep and Glob call passes `` as its path; every file operand of CMD-BOM and CMD-JUNIT-DELETE is absolute (`/`). Evidence records the placeholder, never the host path (C4). If the Bash tool refuses a pwsh invocation, the executor uses the stated fallback and records `PWSH-REFUSED: `; the fallback is not a deviation. - **C6 — Git gate scoping.** Every `git diff` is anchored at `` (P0-T2). Every footprint evaluation removes paths under `.claude/agent-memory/` and paths listed in the P0-T2 `INHERITED:` capture before comparing against the Write Set. Gitignored paths (`artifacts/`, `coverage/`) never appear in porcelain and need no subtraction. - **C7 — Stop rule.** Where a task says STOP, the executor writes the task's artifact with the observed values, leaves the plan checkbox unchecked, and ends the run with a report to the orchestrator. No stop is a pass. - **C8 — Check-off protocol.** Plan checkboxes are flipped only after the task's artifact exists with every C1 field. issue.md check-offs happen only in P2-T9 to P2-T14, one criterion per task, changing `- [ ]` to `- [x]` and nothing else on the line. **CMD-POSHQC-FORMAT** — MCP `mcp__drm-copilot__run_poshqc_format` with `workspace_root` `` and `scan_folders` `["scripts/dependencies","tests/scripts/dependencies"]`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC format against '' with 2 selected scan folder(s).` The tool reports no file list and exits the same whether or not it rewrote a file, so its observation is CMD-HASHSET taken immediately before and immediately after the call; identical hash sets mean no rewrite. -**CMD-HASHSET** — `git hash-object ` for every `.ps1` and `.psm1` file under `scripts/dependencies/` and `tests/scripts/dependencies/` (the files are enumerated with the Glob tool at run time; 13 at planning time, 15 once the two new files exist). `git hash-object` reads the working-tree file whether or not it is tracked, so an untracked new file is observed. The set is recorded as `HASH ` lines sorted by path. +**CMD-HASHSET** — `git hash-object ` for every `.ps1` and `.psm1` file under `scripts/dependencies/` and `tests/scripts/dependencies/` (the files are enumerated with the Glob tool at run time; 14 at planning time (6 under scripts/dependencies, 8 under tests/scripts/dependencies), 16 once the two new files exist). `git hash-object` reads the working-tree file whether or not it is tracked, so an untracked new file is observed. The set is recorded as `HASH ` lines sorted by path. **CMD-POSHQC-ANALYZE** — MCP `mcp__drm-copilot__run_poshqc_analyze` with the same `workspace_root` and `scan_folders`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC analyze against '' with 2 selected scan folder(s).` The payload carries no count, file or rule; `ok` true is the lint result and is recorded with the line `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. `ok` false is a failed step. -**CMD-JUNIT-DELETE** — `pwsh -NoProfile -Command 'Remove-Item -LiteralPath artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue'` followed by a Glob for `artifacts/pester/pester-junit.xml` that must return nothing. Run immediately before every CMD-POSHQC-TEST so the document read afterwards was written by that run. The pwsh payload contains no double quote. +**CMD-JUNIT-DELETE** — `pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue'` followed by a Glob for `artifacts/pester/pester-junit.xml` that must return nothing. Run immediately before every CMD-POSHQC-TEST so the document read afterwards was written by that run. The pwsh payload contains no double quote. Fallback when pwsh is refused: `rm -f /artifacts/pester/pester-junit.xml` followed by the same Glob. **CMD-POSHQC-TEST** — MCP `mcp__drm-copilot__run_poshqc_test` with `workspace_root` `` and `scan_folders` `["tests/scripts/dependencies"]`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC test against '' with 1 selected scan folder(s).`; a failing run observed on this repository returned `ok` false with summary `Command exited with code 4.` The tool writes `artifacts/pester/pester-junit.xml` (and two coverage documents that instrument none of `scripts/dependencies` and are never read). Every test task records the payload verbatim (C3, C4), then runs CMD-JUNIT-READ, and carries the literal line `COVERAGE-MEASUREMENT: deferred to the CI Pester job (_pester.yml); no local figure`. **CMD-JUNIT-READ** — Over `artifacts/pester/pester-junit.xml`, which must exist (Glob) after the run: (a) Grep pattern ` failures= errors= disabled=` from that line's attributes; (b) Grep pattern ` tests= failures= skipped=` line per match, the leaf taken after the last path separator so no host path is transcribed; (c) Grep pattern `status="Failed"` with `-A 2` → one `JUNIT-NOTPASSED ` line per matched testcase and a `JUNIT-MESSAGE ` line, with any worktree prefix replaced per C4; when (c) returns no match the artifact states `JUNIT-NOTPASSED: none`. Expected suite leaf names at planning time: AnalyzerItemRepair.Tests.ps1, ConsistencyVerifier.Tests.ps1, DependabotConfig.Tests.ps1, PackageCompatibility.Tests.ps1, PackageGraph.Tests.ps1, ProjectConsistency.Tests.ps1, Repair-PackageManifestConsistency.Tests.ps1, RepositoryTreeConsistency.Tests.ps1 and, from P1-T2, BindingRedirectVerification.Tests.ps1. Suite counts are recorded as observed at P0-T12 and compared against that record later; this plan pins no pre-existing suite's count. -**CMD-EOL ``** — `git ls-files --eol -- `. Output is one line of the form `i/ w/ attr/ `. The gate reads the second field, which describes the working-tree file's terminators: `w/crlf` passes; `w/lf`, `w/mixed`, `w/none` or `w/-text` fails. The first field is recorded and not gated. +**CMD-EOL ``** — `git ls-files --eol -- `. Output is one line of the form `i/ w/ attr/` separated by runs of spaces, then one tab and `` (observed: `i/lf w/crlf attr/text=auto` then a tab and the path). The gate reads the second whitespace-separated field, which describes the working-tree file's terminators: `w/crlf` passes; `w/lf`, `w/mixed`, `w/none` or `w/-text` fails. The first field is recorded and not gated. -**CMD-BOM ``** — `pwsh -NoProfile -Command '(Get-Content -LiteralPath -AsByteStream -TotalCount 3) -join [char]44'` with the current directory at the worktree root. A UTF-8 BOM prints `239,187,191`. The payload contains no double quote; `[char]44` is the comma separator. Recorded as `BOM-BYTES =`. +**CMD-BOM ``** — `pwsh -NoProfile -Command '(Get-Content -LiteralPath / -AsByteStream -TotalCount 3) -join [char]44'`. A UTF-8 BOM prints `239,187,191`. The payload contains no double quote; `[char]44` is the comma separator. Recorded as `BOM-BYTES =`. Fallback when pwsh is refused: `od -A n -t u1 -N 3 /`, whose success-case output is ` 239 187 191` (observed at preflight); recorded after whitespace normalisation as `BOM-BYTES =239,187,191`, so the two routes yield the same recorded value. **CMD-NUMSTAT ``** — `git diff --numstat -- `. For a one-line replacement the output is exactly `1 1 ` (tab separated). Paired in the same task with `git diff -- `, whose content lines (those beginning `-` or `+` after the `---`/`+++` header pair) must be exactly one removed line and one added line. @@ -131,7 +131,7 @@ and new_string equal to the same two lines with the second replaced by: ``` -Acceptance for every EDIT-FIZZLER task (all six must hold): (1) CMD-NUMSTAT prints exactly `1 1 `; (2) `git diff -- ` shows exactly one `-` content line, whose text ignoring a trailing carriage return is ` `, and exactly one `+` content line, whose text ignoring a trailing carriage return is ` `; (3) CMD-EOL prints `w/crlf` in the second field; (4) CMD-BOM prints a value equal to the `BOM-BYTES` value P0-T7 recorded for the same path (expected `239,187,191`); (5) Grep `name="Fizzler"` with `-A 1` on `` shows the redirect line now reading `1.3.1.0` in both attributes; (6) Grep `name="System.ClientModel"` with `-A 1` on `` shows the System.ClientModel redirect line unchanged from P0-T5 (1.3.0.0 in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; 1.16.0.0 in the five test configs). The task artifact goes to `evidence/qa-gates/` with `Command:` naming the Edit and the four observation commands and `EXIT_CODE: 0`. If the Edit tool reports no match, STOP: EDIT-MISMATCH (D2). +Acceptance for every EDIT-FIZZLER task (all six must hold): (1) CMD-NUMSTAT prints exactly `1 1 `; (2) `git diff -- ` shows exactly one `-` content line, whose text ignoring a trailing carriage return is ` `, and exactly one `+` content line, whose text ignoring a trailing carriage return is ` `; (3) CMD-EOL prints `w/crlf` in the second whitespace-separated field; (4) CMD-BOM prints a value equal to the `BOM-BYTES` value P0-T7 recorded for the same path (expected `239,187,191`); (5) Grep `name="Fizzler"` with `-A 1` on `` shows the redirect line now reading `1.3.1.0` in both attributes; (6) Grep `name="System.ClientModel"` with `-A 1` on `` shows the System.ClientModel redirect line unchanged from P0-T5 (1.3.0.0 in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; 1.16.0.0 in the five test configs). The task artifact goes to `evidence/qa-gates/` with `Command:` naming the Edit and the four observation commands and `EXIT_CODE: 0`. If the Edit tool reports no match, STOP: EDIT-MISMATCH (D2). ## 7. Known-debt set (orchestrator measurement 2026-10-02, re-derived by the planner 2026-10-02, re-measured by P0-T8) @@ -161,6 +161,8 @@ Scope boundary: correcting these 15 pairs is out of scope for issue 953. P2-T16 ASCII-only content (so `PSUseBOMForUnicodeEncodedFile` cannot fire), target 100 to 150 lines, hard ceiling 500. Comment-based help on both functions. Written as follows; the executor reproduces this content, adjusting only formatting that the PoshQC formatter requires. +Note on the empty-element path of `ConvertTo-ReferenceVersionMap`: a Mandatory `[string[]]` parameter rejects an empty-string element at binding unless it carries `[AllowEmptyString()]`, so the attribute is present and an empty or whitespace-only element reaches `ConvertFrom-ProjectFileText`, which throws for it (PackageGraph.psm1 lines 230-236). No section 9 test supplies such an element: the throw is the parser's own behaviour and PackageGraph.Tests.ps1 line 268 (`rejects whitespace-only project-file text`) already covers it. The attribute adds no executable line, so it has no coverage effect. + ```powershell <# .SYNOPSIS @@ -201,7 +203,8 @@ function ConvertTo-ReferenceVersionMap { Only an Include of the form "Name, Version=X.Y.Z.W, ..." contributes. A Reference without a Version is not evidence of a deployed version and is skipped. Versions are unioned across every supplied text, so an assembly two projects reference at two - versions maps to both. Empty or whitespace project text is rejected by the parser. + versions maps to both. An empty or whitespace-only element is accepted at parameter + binding and then rejected by ConvertFrom-ProjectFileText, which throws for it. .PARAMETER ProjectText The project-file texts. An empty collection yields an empty map. #> @@ -210,6 +213,7 @@ function ConvertTo-ReferenceVersionMap { param( [Parameter(Mandatory = $true)] [AllowEmptyCollection()] + [AllowEmptyString()] [string[]]$ProjectText ) @@ -303,7 +307,7 @@ Export-ModuleMember -Function @( ## 9. Test specification (`tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`) -ASCII-only, under 500 lines, `Set-StrictMode -Version Latest` at line 1, Arrange-Act-Assert comments in every It, one behaviour per It. `BeforeAll` resolves `$script:RepoRoot` from `$PSScriptRoot` exactly as RepositoryTreeConsistency.Tests.ps1 line 4 does, imports `scripts/dependencies/PackageGraph.psm1` with `-Force` and then `scripts/dependencies/BindingRedirectVerification.psm1` with `-Force` (the parser's functions are needed in test scope by test 13 and are not re-exported by the new module), defines a `ConvertTo-CrLf` helper as PackageGraph.Tests.ps1 lines 10-13 do, and holds every fixture as an in-memory string: a configuration wrapper (`...`) around dependentAssembly blocks for Fizzler at 1.3.0.0 (`oldVersion="0.0.0.0-1.3.0.0"`), Fizzler at 1.3.1.0, Fizzler at 1.3.1.0 with `oldVersion="0.0.0.0-9.9.9.9"`, System.Runtime.CompilerServices.Unsafe at 6.0.3.0, an assembly named Contoso.Unknown at 1.0.0.0, and a block with an assemblyIdentity (Contoso.NoRedirect) and no bindingRedirect; a provider scriptblock returning `@('1.3.1.0')` for Fizzler, `@('6.0.3.0')` for System.Runtime.CompilerServices.Unsafe and `@()` otherwise; a second provider returning `@('1.3.0.0','1.3.1.0')` for Fizzler; and two project-file fixtures in the shape of PackageGraph.Tests.ps1 lines 71-85 (ProjectA with `` and ``; ProjectB with ``). No `$TestDrive`, `New-Item`, `Set-Content`, `Out-File`, `Add-Content` or `New-TemporaryFile` appears anywhere in the file. +ASCII-only, under 500 lines, `Set-StrictMode -Version Latest` at line 1, Arrange-Act-Assert comments in every It, one behaviour per It. `BeforeAll` resolves `$script:RepoRoot` from `$PSScriptRoot` exactly as RepositoryTreeConsistency.Tests.ps1 line 4 does, imports `scripts/dependencies/PackageGraph.psm1` with `-Force` and then `scripts/dependencies/BindingRedirectVerification.psm1` with `-Force` (the parser's functions are needed in test scope by test 13 and are not re-exported by the new module) using exactly these two lines, in this order: `Import-Module (Join-Path $script:RepoRoot 'scripts/dependencies/PackageGraph.psm1') -Force` and `Import-Module (Join-Path $script:RepoRoot 'scripts/dependencies/BindingRedirectVerification.psm1') -Force` (the RepositoryTreeConsistency.Tests.ps1 line 5 form). Every fixture and provider defined in BeforeAll is assigned to a `$script:` variable. BeforeAll also defines a `ConvertTo-CrLf` helper as PackageGraph.Tests.ps1 lines 10-13 do, and holds every fixture as an in-memory string: a configuration wrapper (`...`) around dependentAssembly blocks for Fizzler at 1.3.0.0 (`oldVersion="0.0.0.0-1.3.0.0"`), Fizzler at 1.3.1.0, Fizzler at 1.3.1.0 with `oldVersion="0.0.0.0-9.9.9.9"`, System.Runtime.CompilerServices.Unsafe at 6.0.3.0, an assembly named Contoso.Unknown at 1.0.0.0, and a block with an assemblyIdentity (Contoso.NoRedirect) and no bindingRedirect; a provider scriptblock returning `@('1.3.1.0')` for Fizzler, `@('6.0.3.0')` for System.Runtime.CompilerServices.Unsafe and `@()` otherwise; a second provider returning `@('1.3.0.0','1.3.1.0')` for Fizzler; and two project-file fixtures in the shape of PackageGraph.Tests.ps1 lines 71-85 (ProjectA with `` and ``; ProjectB with ``). No `$TestDrive`, `New-Item`, `Set-Content`, `Out-File`, `Add-Content` or `New-TemporaryFile` appears anywhere in the file. The 14 It names, verbatim, with the assertion each carries: @@ -328,7 +332,7 @@ Describe 'ConvertTo-ReferenceVersionMap (in-memory fixtures)': Describe 'Repository binding redirects (issue 953)' (reads tracked files read-only through `[System.IO.File]::ReadAllText` on paths derived from `$script:RepoRoot`; writes nothing): 13. `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` — Arrange: `$configPath` is every `app.config` directly under a root-level directory (`Get-ChildItem -LiteralPath $script:RepoRoot -Directory`, `Join-Path ... 'app.config'`, `Test-Path -LiteralPath`); `$configPath.Count | Should -BeGreaterThan 9`. Act: for each config, `ConvertFrom-AppConfigText` records whose `Name` is `Fizzler`, projected to `Config` (leaf directory name), `NewVersion`, `OldVersion`; `$stale` is those with `NewVersion -ne '1.3.1.0' -or OldVersion -ne '0.0.0.0-1.3.1.0'`. Assert: total Fizzler records `Should -Be 13 -Because 'thirteen configs carry a Fizzler redirect'`; `$stale.Count | Should -Be 0 -Because ('these configs redirect Fizzler to another version: ' + (($stale | ForEach-Object { $_.Config + '=' + $_.NewVersion }) -join '; '))`. This is the [expect-fail] regression test: before Phase 1 it fails with `Expected 0, because these configs redirect Fizzler to another version: ...=1.3.0.0; ..., but got 11.` naming the eleven directories; after P1-T14 it passes. -14. `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference` — Arrange: `$configPath` as in test 13; `$projectPath` is every `*.csproj` directly under a root-level directory; both counts `Should -BeGreaterThan 9`; `$map = ConvertTo-ReferenceVersionMap -ProjectText @($projectPath | ForEach-Object { [System.IO.File]::ReadAllText($_) })`; `$provider = { param($Name) $map[$Name] }.GetNewClosure()`; `$expectedDebt` is the 15 strings `Name|NewVersion` from section 7; `$expectedUnverifiable` is `@('Microsoft.IdentityModel.Clients.ActiveDirectory','System.Linq.AsyncEnumerable','netstandard')`. Act: for each config text, add `[regex]::Matches($text, ', but got @(...).`, so the observed entries sit between `observed: ` and `, but got`, separated by `; `); `$actualUnverifiable | Should -Be @($expectedUnverifiable | Sort-Object -Unique)`; `@($actualDebt | Where-Object { $_ -like 'Fizzler|*' -or $_ -like 'System.Runtime.CompilerServices.Unsafe|*' }).Count | Should -Be 0`. Before Phase 1 this test fails because `Fizzler|1.3.0.0` is a sixteenth pair, and the observed list in its failure message then carries exactly 16 entries (the 15 section 7 pairs plus `Fizzler|1.3.0.0`); after P1-T14 it passes. A new mismatch, a corrected known-debt entry or a new unverifiable name fails it. ## 10. Phases @@ -338,21 +342,21 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on - [ ] [P0-T2] Record the baseline git anchor for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: run `git fetch origin main` (record `FETCH-EXIT:`; a non-zero value is recorded and the run continues against the local `origin/main` ref with the line `FETCH-STALE: local origin/main used`), then `git merge-base HEAD origin/main` → `BASE_SHA:`, `git rev-parse HEAD` → `P0-START:`, `git cat-file -t `, `git status --porcelain --untracked-files=all` → `BASE-PORCELAIN:` (verbatim), `git diff --name-only -- .` → `INHERITED:` (verbatim; this is the set every later footprint evaluation subtracts, C6). Acceptance: BASE_SHA is 40 hexadecimal characters and `git cat-file -t` prints `commit`; no path in BASE-PORCELAIN or INHERITED ends in `.cs`, `.csproj`, `packages.config` or `app.config`, and none lies under `scripts/dependencies/` or `tests/scripts/dependencies/` (the Write Set is clean at start); the feature folder paths and `.claude/agent-memory/` paths are expected and recorded, never asserted empty. Artifact `evidence/baseline/p0-t2-base-anchor..md`. - [ ] [P0-T3] Verify the acceptance-criteria source and mode as a baseline precondition by reading `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md`: line 9 reads `- Work Mode: minor-audit`; exactly one `## Acceptance Criteria` heading (Grep count of `^## Acceptance Criteria$` is 1); Grep count of `^- \[ \] AC[1-6]:` is 6 and of `^- \[x\] AC[1-6]:` is 0; the AC6 line contains the literal `is not measured locally`; Glob for `spec.md` and `user-story.md` in the feature folder returns nothing. Any failed condition is STOP: AC-SOURCE. Artifact `evidence/baseline/p0-t3-ac-precondition..md`. - [ ] [P0-T4] Baseline Fizzler census over `*/app.config`: Grep pattern `name="Fizzler"` with `-A 1`, glob `*/app.config`, content mode. Acceptance: 13 blocks; the following line reads `newVersion="1.3.0.0"` for exactly these 11 files at these lines — QuickFiler/app.config 51, QuickFiler.Test/app.config 47, SVGControl.Test/app.config 19, Tags/app.config 47, TaskMaster/app.config 51, TaskTree/app.config 47, TaskVisualization/app.config 47, TaskVisualization.Test/app.config 47, ToDoModel/app.config 52, ToDoModel.Test/app.config 47, UtilitiesCS.Test/app.config 47 — and reads `newVersion="1.3.1.0"` for SVGControl/app.config 15 and UtilitiesCS/app.config 52. A different member set or line is STOP: TREE-DRIFT (the Write Set line citations would be wrong). Artifact `evidence/baseline/p0-t4-fizzler-census..md`. -- [ ] [P0-T5] Baseline shared-string census over `*/app.config`: Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → expected 17 occurrences across 11 files, 2 each in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel and 1 each in QuickFiler.Test, SVGControl.Test, TaskVisualization.Test, ToDoModel.Test, UtilitiesCS.Test; Grep pattern `name="System.ClientModel"` with `-A 1` → 17 blocks, the following line at 1.3.0.0 for exactly Tags 103, TaskVisualization 103, TaskTree 103, QuickFiler 103, TaskMaster 115, ToDoModel 108 and at 1.16.0.0 for the other 11. Acceptance: both expectations hold (otherwise STOP: TREE-DRIFT). The artifact records the per-file System.ClientModel values as `CLIENTMODEL =` lines for P1 comparison. Artifact `evidence/baseline/p0-t5-shared-string-census..md`. +- [ ] [P0-T5] Baseline shared-string census over `*/app.config`: Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → expected 17 occurrences across 11 files, 2 each in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel and 1 each in QuickFiler.Test, SVGControl.Test, TaskVisualization.Test, ToDoModel.Test, UtilitiesCS.Test; Grep pattern `name="System.ClientModel"` with `-A 1` → 16 blocks (SVGControl/app.config carries none), the following line at 1.3.0.0 for exactly Tags 103, TaskVisualization 103, TaskTree 103, QuickFiler 103, TaskMaster 115, ToDoModel 108 and at 1.16.0.0 for the other 10. Acceptance: both expectations hold (otherwise STOP: TREE-DRIFT). The artifact records the per-file System.ClientModel values as `CLIENTMODEL =` lines for P1 comparison. Artifact `evidence/baseline/p0-t5-shared-string-census..md`. - [ ] [P0-T6] Baseline Unsafe census over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1`, glob `*/app.config`. Acceptance: 17 blocks and every following line reads `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; the artifact lists the 17 files. Artifact `evidence/baseline/p0-t6-unsafe-census..md`. -- [ ] [P0-T7] Baseline encoding observation for the 11 Write Set configs (`QuickFiler/app.config` and the ten others of section 5): for each path run CMD-EOL (second field must read `w/crlf`), CMD-BOM (record `BOM-BYTES =`; expected `239,187,191`; a different value is recorded, not a stop, and becomes the preservation reference for Phase 1), and CMD-LINECOUNT paired with Grep pattern `\r$` count mode on the same file (the two counts must be equal; expected values QuickFiler 243, QuickFiler.Test 367, SVGControl.Test 227, Tags 239, TaskMaster 430, TaskTree 239, TaskVisualization 239, TaskVisualization.Test 363, ToDoModel 323, ToDoModel.Test 363, UtilitiesCS.Test 383). Acceptance: 11 `w/crlf` lines, 11 BOM-BYTES lines, 11 equal count pairs. Artifact `evidence/baseline/p0-t7-encoding-baseline..md`. -- [ ] [P0-T8] Baseline re-measurement of the known-debt table over `*/app.config` and `*/*.csproj`: (a) Grep count mode, glob `*/app.config`, pattern `assemblyIdentity name=` → 1176 total across 17 files; pattern `` → no match, and Grep glob `*/app.config`, pattern `name=""` count mode → 15, 13, 1 respectively. Acceptance: every figure equals section 7; any difference is recorded as `KNOWN-DEBT-DRIFT: ` and the run stops (D7). Completeness beyond the 18 names is proven by test 14 at P1-T15, not here. Artifact `evidence/baseline/p0-t8-known-debt-remeasure..md`. +- [ ] [P0-T7] Baseline encoding observation for the 11 Write Set configs (`QuickFiler/app.config` and the ten others of section 5): for each path run CMD-EOL (second whitespace-separated field must read `w/crlf`), CMD-BOM (record `BOM-BYTES =`; expected `239,187,191`; a different value is recorded, not a stop, and becomes the preservation reference for Phase 1), and CMD-LINECOUNT paired with Grep pattern `\r$` count mode on the same file (the two counts must be equal; expected values QuickFiler 243, QuickFiler.Test 367, SVGControl.Test 227, Tags 239, TaskMaster 430, TaskTree 239, TaskVisualization 239, TaskVisualization.Test 363, ToDoModel 323, ToDoModel.Test 363, UtilitiesCS.Test 383). Acceptance: 11 `w/crlf` lines, 11 BOM-BYTES lines, 11 equal count pairs. Artifact `evidence/baseline/p0-t7-encoding-baseline..md`. +- [ ] [P0-T8] Baseline re-measurement of the known-debt table over `*/app.config` and `*/*.csproj`: (a) Grep count mode, glob `*/app.config`, pattern `assemblyIdentity name=` → 1176 total across 17 files; pattern `` and no other version is printed; (c) for each of the 3 unverifiable names, Grep glob `*/*.csproj`, pattern `Include="` → no match, and Grep glob `*/app.config`, pattern `name=""` count mode → 15, 13, 1 respectively. Acceptance: every figure equals section 7; any difference is recorded as `KNOWN-DEBT-DRIFT: ` and the run stops (D7). Completeness beyond the 18 names is proven by test 14 at P1-T15, not here. Artifact `evidence/baseline/p0-t8-known-debt-remeasure..md`. - [ ] [P0-T9] Baseline line counts (CMD-LINECOUNT) for `scripts/dependencies/PackageGraph.psm1` (465), `scripts/dependencies/ProjectConsistency.psm1` (381), `scripts/dependencies/ConsistencyVerifier.psm1` (499), `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152), and Glob confirmation that `scripts/dependencies/BindingRedirectVerification.psm1` and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` do not yet exist. Acceptance: the four counts equal the figures in parentheses (a difference is recorded as `LINECOUNT-DRIFT:` and the run continues, because none of the four is edited) and both Globs return nothing (a hit is STOP: PREEXISTING-FILE). Artifact `evidence/baseline/p0-t9-linecount-baseline..md`. - [ ] [P0-T10] Baseline PowerShell format step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, then CMD-POSHQC-FORMAT, then CMD-HASHSET again and `git status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies`. Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical and the porcelain over the two folders is empty (the Write Set is clean per P0-T2 and the new files do not exist yet). If a hash differs, the formatter rewrote pre-existing drift outside this item's change: record the paths as `FORMAT-DRIFT-REVERTED:`, restore each with `git checkout -- `, re-run CMD-HASHSET to confirm equality with the first set, and continue. `EXIT_CODE:` per C3. Artifact `evidence/baseline/p0-t10-poshqc-format..md`. - [ ] [P0-T11] Baseline PowerShell analyze step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance: `ok` true with the 2-folder summary literal (the folders were analyzer-clean on 2026-09-30 per issue 929 evidence); `ok` false is STOP: ANALYZE-BASELINE-RED, because pre-existing analyzer debt in the scan scope is outside this plan and would make P2-T2 unsatisfiable. Record `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. Artifact `evidence/baseline/p0-t11-poshqc-analyze..md`. -- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. +- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. - [ ] [P0-T13] Record the PowerShell budget baseline from `.claude/hooks/enforce-powershell-batch-budget.ps1`: quote lines 10-11 (caps), 14 (state file location) and 42-45 (deny behaviour); Glob `.claude/state/powershell-batch-budget.*.json` and record whether any state file exists (read-only; never opened for writing); restate the D9 stop rule in the artifact. Acceptance: the artifact quotes the three regions and carries the line `BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable`. Artifact `evidence/baseline/p0-t13-budget-baseline..md`. ### Phase 1 — Implementation: detector module, regression tests, Fizzler sweep -- [ ] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `-Force` count 0. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. -- [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `Import-Module .*PackageGraph\.psm1.* -Force` count 1 and `Import-Module .*BindingRedirectVerification\.psm1.* -Force` count 1, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. -- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12 is a defect in the module or tests: fix the new file concerned, record the correction, and re-run this task as `.iter2`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. +- [ ] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `^Import-Module ` count 1 and Grep pattern `^Import-Module .*Force` count 0 (the import statement carries no -Force; the explanatory comment at the top of the module mentions -Force and is not gated). A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. +- [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/PackageGraph\.psm1'\) -Force$` count 1 and Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/BindingRedirectVerification\.psm1'\) -Force$` count 1 (the two section 9 import lines), Grep pattern `^\s*Import-Module ` count 2, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. +- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12 is a defect in the module or tests: fix the new file concerned, record the correction, and re-run this task as `.iter2`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. - [ ] [P1-T4] EDIT-FIZZLER `QuickFiler/app.config` lines 50-51; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t4-quickfiler-redirect..md`. - [ ] [P1-T5] EDIT-FIZZLER `QuickFiler.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t5-quickfiler-test-redirect..md`. - [ ] [P1-T6] EDIT-FIZZLER `SVGControl.Test/app.config` lines 18-19; System.ClientModel at 46-47 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t6-svgcontrol-test-redirect..md`. @@ -372,13 +376,13 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 returned `ok` false, or P2-T3 reported any failure, the executor fixes only Write Set PowerShell files (a rewrite of a file outside the Write Set is pre-existing drift: record it, restore with `git checkout -- `, continue) and restarts at P2-T1 as iteration N+1 with `.iter` artifacts. P2-T4 closes the loop only when one iteration shows no rewrite, `ok` true and zero failures together. No step may be recorded SKIPPED. -- [ ] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (15 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. +- [ ] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (16 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. - [ ] [P2-T2] Final QC analyze step (toolchain step 2) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal and the `GATE-SUBSTITUTION:` line. `ok` false: fix the new files only and restart at P2-T1. Type checking is not applicable to PowerShell (`.claude/rules/powershell.md` line 17) and is recorded as such in the artifact. Artifact `evidence/qa-gates/p2-t2-poshqc-analyze.iter..md`. - [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. - [ ] [P2-T4] Loop closure record for the QC toolchain under `evidence/qa-gates/`: list every iteration run (P2-T1, P2-T2, P2-T3 artifact names) and name the terminal iteration N on which P2-T1 recorded `REWRITE-COUNT: 0`, P2-T2 `ok` true and P2-T3 `failures=0` together. Acceptance: the three terminal-iteration artifacts exist (Glob) and carry those values. Artifact `evidence/qa-gates/p2-t4-loop-closure..md`. -- [ ] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10, 12), negative (11) and edge (14, through the repository-wide map) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. +- [ ] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10), negative (11) and edge (12, one assembly at two versions across several project texts) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. - [ ] [P2-T6] File-size audit over `scripts/dependencies/` and `tests/scripts/dependencies/` after the terminal format pass: CMD-LINECOUNT for the two new files and the four P0-T9 neighbours. Acceptance: `BindingRedirectVerification.psm1` below 500 (record the value with `TARGET-BAND: 100-150` and whether it lies inside), `BindingRedirectVerification.Tests.ps1` below 500, and the four neighbour counts equal their P0-T9 values (they are not edited). Markdown under the feature folder is exempt from the 500-line cap per `.claude/rules/general-code-change.md`. Artifact `evidence/qa-gates/p2-t6-file-size-audit..md`. -- [ ] [P2-T7] No-C#-toolchain scope proof over the worktree: `git diff --name-only -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' 'packages.config'` prints nothing, paired with `git status --porcelain -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' 'packages.config'` printing nothing. Acceptance: both empty; the artifact states `CSHARP-TOOLCHAIN: not applicable; no C# source, project, solution or manifest file changed (D11)`. Artifact `evidence/qa-gates/p2-t7-no-csharp-scope..md`. +- [ ] [P2-T7] No-C#-toolchain scope proof over the worktree: `git diff --name-only -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'` prints nothing, paired with `git status --porcelain -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'` printing nothing (the leading `*` makes the pathspec match a packages.config in any project directory; a bare `packages.config` matches only the repository root). Acceptance: both empty; the artifact states `CSHARP-TOOLCHAIN: not applicable; no C# source, project, solution or manifest file changed (D11)`. Artifact `evidence/qa-gates/p2-t7-no-csharp-scope..md`. - [ ] [P2-T8] Footprint assertion over the worktree (CMD-FOOTPRINT): the evaluated set (C6) must equal the union of the 11 Write Set configs, `scripts/dependencies/BindingRedirectVerification.psm1`, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` and paths under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`. Acceptance: every evaluated path is in that union (any other path is STOP: FOOTPRINT, never reverted by this executor) and, as the positive control, all 13 named source paths are present in the evaluated set. Both captures are recorded verbatim per C4; the agent-memory subtraction is stated by composition, not by count. Artifact `evidence/qa-gates/p2-t8-footprint..md`. - [ ] [P2-T9] Check off AC1 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 84, `- [ ] AC1:` to `- [x] AC1:`) citing P1-T4 to P1-T14 (one-line change, `w/crlf`, BOM preserved per file), P1-T16 (13 at 1.3.1.0, 11 changed paths) and P0-T7. Acceptance: Grep count of `^- \[x\] AC1:` in issue.md is 1 and the criterion text after the marker is unchanged. Artifact `evidence/qa-gates/p2-t9-ac1-checkoff..md`. - [ ] [P2-T10] Check off AC2 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 85) citing P0-T6 and P1-T17. Acceptance: Grep count of `^- \[x\] AC2:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t10-ac2-checkoff..md`. @@ -388,7 +392,7 @@ The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 retu - [ ] [P2-T14] Check off AC6 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 89) citing P2-T1 to P2-T5 and quoting the AC6 literal `is not measured locally` as the authority for the CI coverage deferral (D8). Acceptance: Grep count of `^- \[x\] AC6:` is 1 and the text is unchanged; the artifact carries `COVERAGE-SOURCE: CI`. Artifact `evidence/qa-gates/p2-t14-ac6-checkoff..md`. - [ ] [P2-T15] Acceptance-criteria status summary and plan reconciliation for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: Grep count of `^- \[x\] AC[1-6]:` in issue.md is 6 and of `^- \[ \] AC[1-6]:` is 0; the artifact carries the `### Acceptance Criteria Status` block (Source, Total AC items: 6, Checked off: 6, Remaining: 0). Acceptance: both counts hold. Artifact `evidence/qa-gates/p2-t15-ac-status..md`. - [ ] [P2-T16] Follow-up note for the coordinator under `evidence/other/`: the 15 known-debt pairs of section 7 with their config counts and Reference versions, the 3 unverifiable names, the statement that correcting them is out of scope for issue 953, and the statement that this plan created no issue and no potential entry (the coordinator promotes). Acceptance: the artifact lists all 15 pairs and 3 names and carries `FOLLOW-UP: known-debt redirect correction, 15 pairs, 137 entries; promotion by the coordinator`. Artifact `evidence/other/p2-t16-known-debt-followup..md`. -- [ ] [P2-T17] Reduced-audit handoff index under `evidence/other/` for the QC and test evidence: `BASE_SHA:`, the Write Set, `COVERAGE-SOURCE: CI`, every `GATE-SUBSTITUTION:` line used, the budget outcome (no denial, or the denial record), the terminal loop iteration N, the final suite counts, and the path of every artifact written by P0-T1 through P2-T16 (bounded there; this artifact names itself only as the index). Acceptance: every artifact path named by P0-T1 through P2-T16 exists (Glob) and is listed; the index carries the literal `AUDIT-MODE: reduced (minor-audit)`. Artifact `evidence/other/p2-t17-reduced-audit-handoff..md`. +- [ ] [P2-T17] Reduced-audit handoff index under `evidence/other/` for the QC and test evidence: `BASE_SHA:`, the Write Set, `COVERAGE-SOURCE: CI`, every `GATE-SUBSTITUTION:` line used, the budget outcome (no denial, or the denial record), the terminal loop iteration N, the final suite counts, and the path of every artifact written by P0-T1 through P2-T16 (bounded there; this artifact names itself only as the index). The index cites D8 as this plan's exception to the atomic-plan-contract Coverage Evidence Contract: no numeric baseline or post-change coverage figure is recorded locally because no local Pester coverage route instruments `scripts/dependencies`, and the figure is read from the CI Pester job. Acceptance: every artifact path named by P0-T1 through P2-T16 exists (Glob) and is listed; the index carries the literals `AUDIT-MODE: reduced (minor-audit)` and `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`. Artifact `evidence/other/p2-t17-reduced-audit-handoff..md`. ## 11. Traceability @@ -409,6 +413,7 @@ The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 retu 4. **Coverage figure.** No local Pester line-coverage figure exists for `scripts/dependencies` through the MCP route. The CI Pester job supplies it on the pushed head; the handoff carries `COVERAGE-SOURCE: CI`. A reviewer who requires a local figure must raise it against AC6's wording, not against this plan. 5. **MCP test tool exit.** The tool's payload `ok` has been observed false with `Command exited with code 4.` on a red suite and true on a green one in this repository; its process exit code is not observable and is not claimed (C3). 6. **Hook-required preflight line.** The planner's output carries a `PREFLIGHT: REVISIONS REQUIRED` line because the SubagentStop hook bounds the internal-review record with it; the line records that executor preflight is outstanding and is not a discovered defect or a self-approval. +7. **pwsh refusal under worktree isolation.** The round 1 preflight reviewer observed the Bash tool refuse a pwsh one-liner in this worktree while `od`, `rm -f` and `git -C` ran. CMD-BOM and CMD-JUNIT-DELETE carry those fallbacks (C5); a run that uses them records `PWSH-REFUSED:` with the tool message and is not a deviation. ## 13. Planner internal review @@ -427,15 +432,17 @@ CITATION: TaskVisualization.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 102-1 CITATION: ToDoModel/app.config | lines 51-52 Fizzler 1.3.0.0; 107-108 System.ClientModel 1.3.0.0 CITATION: ToDoModel.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 System.ClientModel 1.16.0.0 CITATION: UtilitiesCS.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 122-123 System.ClientModel 1.16.0.0 -CITATION: SVGControl/app.config | lines 14-15 Fizzler 1.3.1.0 (not edited) -CITATION: UtilitiesCS/app.config | lines 51-52 Fizzler 1.3.1.0 (not edited) +CITATION: SVGControl/app.config | lines 14-15 Fizzler 1.3.1.0 (not edited); no System.ClientModel block +CITATION: UtilitiesCS/app.config | lines 51-52 Fizzler 1.3.1.0 (not edited); 107-108 System.ClientModel 1.16.0.0 +CITATION: VBFunctions.Test/app.config | lines 98-99 System.ClientModel 1.16.0.0 (with TaskMaster.Test 98-99, TaskTree.Test 222-223, Tags.Test 222-223 and the five Write Set test configs: 10 blocks at 1.16.0.0, 16 ClientModel blocks in all) +CITATION: scripts/dependencies | 6 .ps1/.psm1 files by Glob; with the 8 under tests/scripts/dependencies, CMD-HASHSET enumerates 14 at planning time CITATION: SVGControl/SVGControl.csproj | line 58 Reference Fizzler, Version=1.3.1.0 CITATION: UtilitiesCS/UtilitiesCS.csproj | line 65 Reference Fizzler, Version=1.3.1.0; line 305 Reference System.ClientModel, Version=1.16.0.0 -CITATION: scripts/dependencies/PackageGraph.psm1 | ConvertFrom-ProjectFileText line 215; ConvertFrom-AppConfigText line 285; root check lines 304-305; Export-ModuleMember lines 457-465; 465 lines +CITATION: scripts/dependencies/PackageGraph.psm1 | ConvertFrom-ProjectFileText line 215, AllowEmptyString line 230, empty-text throw lines 234-236; ConvertFrom-AppConfigText line 285; root check lines 304-305; Export-ModuleMember lines 457-465; 465 lines CITATION: scripts/dependencies/ProjectConsistency.psm1 | header ownership lines 6-12; import without -Force lines 33-44; Invoke-BindingRedirectReconciliation lines 271-375; 381 lines CITATION: scripts/dependencies/ConsistencyVerifier.psm1 | 499 lines; DetectionResult shape lines 46-54 -CITATION: tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 | RepoRoot line 4; import line 5; discovery guard line 74; examined guard lines 87-89; SVGControl redirect test lines 92-110; 152 lines -CITATION: tests/scripts/dependencies/PackageGraph.Tests.ps1 | ConvertTo-CrLf lines 10-13; app.config fixture lines 41-69; project fixture lines 71-85 +CITATION: tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 | RepoRoot line 4; import line 5 in the form Import-Module (Join-Path $script:RepoRoot '') -Force; discovery guard line 74; examined guard lines 87-89; SVGControl redirect test lines 92-110; 152 lines +CITATION: tests/scripts/dependencies/PackageGraph.Tests.ps1 | ConvertTo-CrLf lines 10-13; app.config fixture lines 41-69; project fixture lines 71-85; whitespace-only project text rejection test line 268 CITATION: .github/workflows/_pester.yml | Run.Path line 41; CodeCoverage.Path line 45; PESTER line 51; COVERAGE line 64; floor line 71 CITATION: .claude/rules/powershell.md | toolchain lines 15-20; change budget lines 37-41; seams lines 43-52 CITATION: .claude/hooks/enforce-powershell-batch-budget.ps1 | caps lines 10-11; state file line 14; override variables lines 37-40; deny lines 42-45 @@ -453,3 +460,5 @@ UNRESOLVED-GAPS: NONE PREFLIGHT: REVISIONS REQUIRED The PREFLIGHT line above is the hook-required terminator of the bounded record. It records that executor preflight has not yet run; it is not a discovered defect and not a self-approval. The orchestrator runs the validator and the atomic-executor preflight. + +Revision round 1 (2026-10-02T02-10), applied against the round 1 preflight report and re-derived against the tree in the same pass: section 4 row 4 and P0-T5 (ClientModel 16 blocks, 6 plus 10); CMD-HASHSET and P2-T1 (14 and 16 hash entries); P1-T1 (Import-Module gates replace the `-Force` count); P0-T8(b) (`-o` pattern reads the version; multiline count read per file); C5, CMD-BOM, CMD-JUNIT-DELETE and residual risk 7 (`git -C`, absolute operands, pwsh-refused fallbacks); CMD-EOL, D3, P0-T7 and EDIT-FIZZLER item (3) (space-padded fields, one tab before the path); P2-T7 (`*packages.config`); section 9 and P1-T2 (exact import lines, `$script:` fixtures); P1-T3 and section 9 test 14 (16-entry observed list); P2-T5 (tests 10, 11, 12); section 8 (`[AllowEmptyString()]` and the corrected help sentence, with the note before the fence); P0-T12 and P2-T17 (D8 named as the Coverage Evidence Contract exception). Task count unchanged at 47. From ded8e035d8f67413fd2a76bdbb76a2cb38044dca Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:46:39 -0400 Subject: [PATCH 06/14] docs(953): apply preflight round 2 revisions to the atomic plan --- .../plan.2026-10-02T00-16.md | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index c093cf57d..d4d2d7cca 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -4,9 +4,9 @@ - **Parent (optional):** none - **Owner:** drmoisan - **Branch:** bug/stale-fizzler-and-unsafe-binding-redirects-953 -- **Last Updated:** 2026-10-02T02-10 -- **Status:** Draft (awaiting validator and executor preflight; revision round 1 applied) -- **Version:** 1.1 +- **Last Updated:** 2026-10-02T02-45 +- **Status:** Draft (awaiting validator and executor preflight; revision rounds 1 and 2 applied) +- **Version:** 1.2 - **Work Mode:** minor-audit (issue.md line 9) - **Task Count:** 47 (Phase 0: 13, Phase 1: 17, Phase 2: 17), counted mechanically over lines matching the task pattern @@ -106,7 +106,7 @@ Nothing else is written. `artifacts/pester/*` is written by the MCP test tool an **CMD-POSHQC-TEST** — MCP `mcp__drm-copilot__run_poshqc_test` with `workspace_root` `` and `scan_folders` `["tests/scripts/dependencies"]`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC test against '' with 1 selected scan folder(s).`; a failing run observed on this repository returned `ok` false with summary `Command exited with code 4.` The tool writes `artifacts/pester/pester-junit.xml` (and two coverage documents that instrument none of `scripts/dependencies` and are never read). Every test task records the payload verbatim (C3, C4), then runs CMD-JUNIT-READ, and carries the literal line `COVERAGE-MEASUREMENT: deferred to the CI Pester job (_pester.yml); no local figure`. -**CMD-JUNIT-READ** — Over `artifacts/pester/pester-junit.xml`, which must exist (Glob) after the run: (a) Grep pattern ` failures= errors= disabled=` from that line's attributes; (b) Grep pattern ` tests= failures= skipped=` line per match, the leaf taken after the last path separator so no host path is transcribed; (c) Grep pattern `status="Failed"` with `-A 2` → one `JUNIT-NOTPASSED ` line per matched testcase and a `JUNIT-MESSAGE ` line, with any worktree prefix replaced per C4; when (c) returns no match the artifact states `JUNIT-NOTPASSED: none`. Expected suite leaf names at planning time: AnalyzerItemRepair.Tests.ps1, ConsistencyVerifier.Tests.ps1, DependabotConfig.Tests.ps1, PackageCompatibility.Tests.ps1, PackageGraph.Tests.ps1, ProjectConsistency.Tests.ps1, Repair-PackageManifestConsistency.Tests.ps1, RepositoryTreeConsistency.Tests.ps1 and, from P1-T2, BindingRedirectVerification.Tests.ps1. Suite counts are recorded as observed at P0-T12 and compared against that record later; this plan pins no pre-existing suite's count. +**CMD-JUNIT-READ** — Over `artifacts/pester/pester-junit.xml`, which must exist (Glob) after the run: (a) Grep pattern ` failures= errors= disabled=` from that line's attributes; (b) Grep pattern ` tests= failures= skipped=` line per match, the leaf taken after the last path separator so no host path is transcribed; (c) Grep pattern `status="Failed"` with `-n` and `-A 2` → one `JUNIT-NOTPASSED ` line per matched testcase, then for each match the Read tool on the same file with `offset` equal to the matched line number and `limit` 3, from whose output the `JUNIT-MESSAGE ` line is transcribed, with any worktree prefix replaced per C4; when (c) returns no match the artifact states `JUNIT-NOTPASSED: none`. Long-line rule: the Grep tool replaces an output line longer than about 500 characters with `[Omitted long matching line]` or `[Omitted long context line]` (observed at preflight round 2 on four lines of version 1.1 of this plan, and re-observed by the planner on version 1.2, where Grep pattern `^.{500,}` over this plan reports every line over 500 characters as omitted, among them the CMD-POSHQC-FORMAT, CMD-JUNIT-DELETE and CMD-JUNIT-READ definitions and section 9 tests 13 and 14); a `testsuite` line carries the absolute test-file path twice, and the `failure` lines of tests 13 and 14 are estimated at about 550 and 1,700 characters. Whenever a Grep in (a), (b) or (c) reports an omitted line, the executor reads that line number with the Read tool (`offset` equal to the line number, `limit` 1) and transcribes from the Read output; the Read tool returned section 9 test 14 of this plan (one line of about 3,000 characters) in full at preflight round 2 and again to the planner in this revision pass. Every task that transcribes JUNIT lines (P0-T12, P1-T3, P1-T15, P2-T3) follows this rule. Expected suite leaf names at planning time: AnalyzerItemRepair.Tests.ps1, ConsistencyVerifier.Tests.ps1, DependabotConfig.Tests.ps1, PackageCompatibility.Tests.ps1, PackageGraph.Tests.ps1, ProjectConsistency.Tests.ps1, Repair-PackageManifestConsistency.Tests.ps1, RepositoryTreeConsistency.Tests.ps1 and, from P1-T2, BindingRedirectVerification.Tests.ps1. Suite counts are recorded as observed at P0-T12 and compared against that record later; this plan pins no pre-existing suite's count. **CMD-EOL ``** — `git ls-files --eol -- `. Output is one line of the form `i/ w/ attr/` separated by runs of spaces, then one tab and `` (observed: `i/lf w/crlf attr/text=auto` then a tab and the path). The gate reads the second whitespace-separated field, which describes the working-tree file's terminators: `w/crlf` passes; `w/lf`, `w/mixed`, `w/none` or `w/-text` fails. The first field is recorded and not gated. @@ -153,7 +153,7 @@ Findings expected after the Fizzler sweep: exactly these 15 pairs, 137 redirect 14. System.IdentityModel.Tokens.Jwt | 8.22.0.0 | 8.23.0.0 | 13 15. System.ClientModel | 1.3.0.0 | 1.16.0.0 | 6 -Unverifiable (no csproj `Reference Include="Name, Version=` anywhere): System.Linq.AsyncEnumerable (15 entries), Microsoft.IdentityModel.Clients.ActiveDirectory (13), netstandard (1) = 29 entries. Before the sweep the finding set additionally contains `Fizzler|1.3.0.0` (11 entries, 148 in all), which is why test 14 is red before Phase 1 and green after. +Unverifiable (no csproj `Reference Include="Name, Version=` anywhere): System.Linq.AsyncEnumerable (15 entries), Microsoft.IdentityModel.Clients.ActiveDirectory (13), netstandard (1) = 29 entries. Before the sweep the finding set additionally contains `Fizzler|1.3.0.0` (11 entries, 148 in all), which is why test 14 is red at P1-T3, before the config edits P1-T4 to P1-T14, and green at P1-T15. Scope boundary: correcting these 15 pairs is out of scope for issue 953. P2-T16 records them as a follow-up note for the coordinator to promote through the potential-entry lifecycle; this plan creates no issue and no file outside the Write Set. @@ -331,8 +331,8 @@ Describe 'ConvertTo-ReferenceVersionMap (in-memory fixtures)': Describe 'Repository binding redirects (issue 953)' (reads tracked files read-only through `[System.IO.File]::ReadAllText` on paths derived from `$script:RepoRoot`; writes nothing): -13. `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` — Arrange: `$configPath` is every `app.config` directly under a root-level directory (`Get-ChildItem -LiteralPath $script:RepoRoot -Directory`, `Join-Path ... 'app.config'`, `Test-Path -LiteralPath`); `$configPath.Count | Should -BeGreaterThan 9`. Act: for each config, `ConvertFrom-AppConfigText` records whose `Name` is `Fizzler`, projected to `Config` (leaf directory name), `NewVersion`, `OldVersion`; `$stale` is those with `NewVersion -ne '1.3.1.0' -or OldVersion -ne '0.0.0.0-1.3.1.0'`. Assert: total Fizzler records `Should -Be 13 -Because 'thirteen configs carry a Fizzler redirect'`; `$stale.Count | Should -Be 0 -Because ('these configs redirect Fizzler to another version: ' + (($stale | ForEach-Object { $_.Config + '=' + $_.NewVersion }) -join '; '))`. This is the [expect-fail] regression test: before Phase 1 it fails with `Expected 0, because these configs redirect Fizzler to another version: ...=1.3.0.0; ..., but got 11.` naming the eleven directories; after P1-T14 it passes. -14. `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference` — Arrange: `$configPath` as in test 13; `$projectPath` is every `*.csproj` directly under a root-level directory; both counts `Should -BeGreaterThan 9`; `$map = ConvertTo-ReferenceVersionMap -ProjectText @($projectPath | ForEach-Object { [System.IO.File]::ReadAllText($_) })`; `$provider = { param($Name) $map[$Name] }.GetNewClosure()`; `$expectedDebt` is the 15 strings `Name|NewVersion` from section 7; `$expectedUnverifiable` is `@('Microsoft.IdentityModel.Clients.ActiveDirectory','System.Linq.AsyncEnumerable','netstandard')`. Act: for each config text, add `[regex]::Matches($text, ', but got @(...).`, so the observed entries sit between `observed: ` and `, but got`, separated by `; `); `$actualUnverifiable | Should -Be @($expectedUnverifiable | Sort-Object -Unique)`; `@($actualDebt | Where-Object { $_ -like 'Fizzler|*' -or $_ -like 'System.Runtime.CompilerServices.Unsafe|*' }).Count | Should -Be 0`. Before Phase 1 this test fails because `Fizzler|1.3.0.0` is a sixteenth pair, and the observed list in its failure message then carries exactly 16 entries (the 15 section 7 pairs plus `Fizzler|1.3.0.0`); after P1-T14 it passes. A new mismatch, a corrected known-debt entry or a new unverifiable name fails it. +13. `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` — Arrange: `$configPath` is every `app.config` directly under a root-level directory (`Get-ChildItem -LiteralPath $script:RepoRoot -Directory`, `Join-Path ... 'app.config'`, `Test-Path -LiteralPath`); `$configPath.Count | Should -BeGreaterThan 9`. Act: for each config, `ConvertFrom-AppConfigText` records whose `Name` is `Fizzler`, projected to `Config` (leaf directory name), `NewVersion`, `OldVersion`; `$stale` is those with `NewVersion -ne '1.3.1.0' -or OldVersion -ne '0.0.0.0-1.3.1.0'`. Assert: total Fizzler records `Should -Be 13 -Because 'thirteen configs carry a Fizzler redirect'`; `$stale.Count | Should -Be 0 -Because ('these configs redirect Fizzler to another version: ' + (($stale | ForEach-Object { $_.Config + '=' + $_.NewVersion }) -join '; '))`. This is the [expect-fail] regression test: at P1-T3, before the config edits, it fails with `Expected 0, because these configs redirect Fizzler to another version: ...=1.3.0.0; ..., but got 11.` naming the eleven directories; after P1-T14 it passes. +14. `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference` — Arrange: `$configPath` as in test 13; `$projectPath` is every `*.csproj` directly under a root-level directory; both counts `Should -BeGreaterThan 9`; `$map = ConvertTo-ReferenceVersionMap -ProjectText @($projectPath | ForEach-Object { [System.IO.File]::ReadAllText($_) })`; `$provider = { param($Name) $map[$Name] }.GetNewClosure()`; `$expectedDebt` is the 15 strings `Name|NewVersion` from section 7; `$expectedUnverifiable` is `@('Microsoft.IdentityModel.Clients.ActiveDirectory','System.Linq.AsyncEnumerable','netstandard')`. Act: for each config text, add `[regex]::Matches($text, ', but got @(...).`, so the observed entries sit between `observed: ` and `, but got`, separated by `; `); `$actualUnverifiable | Should -Be @($expectedUnverifiable | Sort-Object -Unique)`; `@($actualDebt | Where-Object { $_ -like 'Fizzler|*' -or $_ -like 'System.Runtime.CompilerServices.Unsafe|*' }).Count | Should -Be 0`. At P1-T3, before the config edits, this test fails because `Fizzler|1.3.0.0` is a sixteenth pair, and the observed list in its failure message then carries exactly 16 entries (the 15 section 7 pairs plus `Fizzler|1.3.0.0`); after P1-T14 it passes. A new mismatch, a corrected known-debt entry or a new unverifiable name fails it. ## 10. Phases @@ -349,14 +349,14 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on - [ ] [P0-T9] Baseline line counts (CMD-LINECOUNT) for `scripts/dependencies/PackageGraph.psm1` (465), `scripts/dependencies/ProjectConsistency.psm1` (381), `scripts/dependencies/ConsistencyVerifier.psm1` (499), `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152), and Glob confirmation that `scripts/dependencies/BindingRedirectVerification.psm1` and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` do not yet exist. Acceptance: the four counts equal the figures in parentheses (a difference is recorded as `LINECOUNT-DRIFT:` and the run continues, because none of the four is edited) and both Globs return nothing (a hit is STOP: PREEXISTING-FILE). Artifact `evidence/baseline/p0-t9-linecount-baseline..md`. - [ ] [P0-T10] Baseline PowerShell format step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, then CMD-POSHQC-FORMAT, then CMD-HASHSET again and `git status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies`. Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical and the porcelain over the two folders is empty (the Write Set is clean per P0-T2 and the new files do not exist yet). If a hash differs, the formatter rewrote pre-existing drift outside this item's change: record the paths as `FORMAT-DRIFT-REVERTED:`, restore each with `git checkout -- `, re-run CMD-HASHSET to confirm equality with the first set, and continue. `EXIT_CODE:` per C3. Artifact `evidence/baseline/p0-t10-poshqc-format..md`. - [ ] [P0-T11] Baseline PowerShell analyze step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance: `ok` true with the 2-folder summary literal (the folders were analyzer-clean on 2026-09-30 per issue 929 evidence); `ok` false is STOP: ANALYZE-BASELINE-RED, because pre-existing analyzer debt in the scan scope is outside this plan and would make P2-T2 unsatisfiable. Record `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. Artifact `evidence/baseline/p0-t11-poshqc-analyze..md`. -- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. +- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines are transcribed under the CMD-JUNIT-READ long-line rule: any line the Grep tool reports as omitted is read by line number with the Read tool, which is expected for every `testsuite` line because each carries the absolute test-file path twice). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. - [ ] [P0-T13] Record the PowerShell budget baseline from `.claude/hooks/enforce-powershell-batch-budget.ps1`: quote lines 10-11 (caps), 14 (state file location) and 42-45 (deny behaviour); Glob `.claude/state/powershell-batch-budget.*.json` and record whether any state file exists (read-only; never opened for writing); restate the D9 stop rule in the artifact. Acceptance: the artifact quotes the three regions and carries the line `BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable`. Artifact `evidence/baseline/p0-t13-budget-baseline..md`. ### Phase 1 — Implementation: detector module, regression tests, Fizzler sweep - [ ] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `^Import-Module ` count 1 and Grep pattern `^Import-Module .*Force` count 0 (the import statement carries no -Force; the explanatory comment at the top of the module mentions -Force and is not gated). A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. -- [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/PackageGraph\.psm1'\) -Force$` count 1 and Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/BindingRedirectVerification\.psm1'\) -Force$` count 1 (the two section 9 import lines), Grep pattern `^\s*Import-Module ` count 2, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. -- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12 is a defect in the module or tests: fix the new file concerned, record the correction, and re-run this task as `.iter2`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. +- [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/PackageGraph\.psm1'\) -Force\r?$` count 1 and Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/BindingRedirectVerification\.psm1'\) -Force\r?$` count 1 (the two section 9 import lines; the `\r?` admits a carriage return before the line end, because ripgrep's `$` does not match before a carriage return and the terminator the Write tool and the PoshQC formatter leave on this new file is not pinned by this plan; the gate asserts the line content, not the terminator), Grep pattern `^\s*Import-Module ` count 2, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. +- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (the two JUNIT-MESSAGE lines below are transcribed from the Read tool under the CMD-JUNIT-READ long-line rule, because both `failure` lines are expected to exceed the length at which the Grep tool omits a line; the Grep `-n` output supplies the line numbers the Read tool is pointed at). Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12, or a failure of test 13 or 14 whose JUNIT-MESSAGE does not begin `Expected ` and contain `, but got ` (an exception or StrictMode error inside the test body rather than the expected assertion failure), is a defect in a new file: fix the file concerned, record the correction, and re-run this task as `.iter`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. - [ ] [P1-T4] EDIT-FIZZLER `QuickFiler/app.config` lines 50-51; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t4-quickfiler-redirect..md`. - [ ] [P1-T5] EDIT-FIZZLER `QuickFiler.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t5-quickfiler-test-redirect..md`. - [ ] [P1-T6] EDIT-FIZZLER `SVGControl.Test/app.config` lines 18-19; System.ClientModel at 46-47 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t6-svgcontrol-test-redirect..md`. @@ -368,7 +368,7 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on - [ ] [P1-T12] EDIT-FIZZLER `ToDoModel/app.config` lines 51-52; System.ClientModel at 107-108 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t12-todomodel-redirect..md`. - [ ] [P1-T13] EDIT-FIZZLER `ToDoModel.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t13-todomodel-test-redirect..md`. - [ ] [P1-T14] EDIT-FIZZLER `UtilitiesCS.Test/app.config` lines 46-47; System.ClientModel at 122-123 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t14-utilitiescs-test-redirect..md`. -- [ ] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. +- [ ] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. - [ ] [P1-T16] Sweep verification over `*/app.config` (AC1 evidence): Grep pattern `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` glob `*/app.config` count mode → 13 occurrences across 13 files; Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → exactly 6 occurrences across exactly QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; Grep pattern `name="Fizzler"` with `-A 1` → 13 blocks all followed by `1.3.1.0` in both attributes; `git diff --name-only -- '*/app.config'` lists exactly the 11 Write Set config paths, paired with `git status --porcelain -- '*/app.config'` showing exactly 11 ` M` lines for the same paths. Acceptance: all four observations hold. Artifact `evidence/qa-gates/p1-t16-sweep-verification..md`. - [ ] [P1-T17] Unsafe re-verification after the edits over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1` → 17 blocks all `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; `git diff -- '*/app.config'` contains no content line (beginning `-` or `+` after the header pairs) that contains `Unsafe`, and its 22 content lines are the 11 removed and 11 added Fizzler redirect lines. Acceptance: both hold. Artifact `evidence/qa-gates/p1-t17-unsafe-unchanged..md`. @@ -378,7 +378,7 @@ The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 retu - [ ] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (16 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. - [ ] [P2-T2] Final QC analyze step (toolchain step 2) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal and the `GATE-SUBSTITUTION:` line. `ok` false: fix the new files only and restart at P2-T1. Type checking is not applicable to PowerShell (`.claude/rules/powershell.md` line 17) and is recorded as such in the artifact. Artifact `evidence/qa-gates/p2-t2-poshqc-analyze.iter..md`. -- [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ. Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. +- [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. - [ ] [P2-T4] Loop closure record for the QC toolchain under `evidence/qa-gates/`: list every iteration run (P2-T1, P2-T2, P2-T3 artifact names) and name the terminal iteration N on which P2-T1 recorded `REWRITE-COUNT: 0`, P2-T2 `ok` true and P2-T3 `failures=0` together. Acceptance: the three terminal-iteration artifacts exist (Glob) and carry those values. Artifact `evidence/qa-gates/p2-t4-loop-closure..md`. - [ ] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10), negative (11) and edge (12, one assembly at two versions across several project texts) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. - [ ] [P2-T6] File-size audit over `scripts/dependencies/` and `tests/scripts/dependencies/` after the terminal format pass: CMD-LINECOUNT for the two new files and the four P0-T9 neighbours. Acceptance: `BindingRedirectVerification.psm1` below 500 (record the value with `TARGET-BAND: 100-150` and whether it lies inside), `BindingRedirectVerification.Tests.ps1` below 500, and the four neighbour counts equal their P0-T9 values (they are not edited). Markdown under the feature folder is exempt from the 500-line cap per `.claude/rules/general-code-change.md`. Artifact `evidence/qa-gates/p2-t6-file-size-audit..md`. @@ -434,7 +434,7 @@ CITATION: ToDoModel.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 102-103 Syste CITATION: UtilitiesCS.Test/app.config | lines 46-47 Fizzler 1.3.0.0; 122-123 System.ClientModel 1.16.0.0 CITATION: SVGControl/app.config | lines 14-15 Fizzler 1.3.1.0 (not edited); no System.ClientModel block CITATION: UtilitiesCS/app.config | lines 51-52 Fizzler 1.3.1.0 (not edited); 107-108 System.ClientModel 1.16.0.0 -CITATION: VBFunctions.Test/app.config | lines 98-99 System.ClientModel 1.16.0.0 (with TaskMaster.Test 98-99, TaskTree.Test 222-223, Tags.Test 222-223 and the five Write Set test configs: 10 blocks at 1.16.0.0, 16 ClientModel blocks in all) +CITATION: VBFunctions.Test/app.config | lines 98-99 System.ClientModel 1.16.0.0 (with TaskMaster.Test 98-99, TaskTree.Test 222-223, Tags.Test 222-223, UtilitiesCS 107-108 and the five Write Set test configs: 10 blocks at 1.16.0.0, 16 ClientModel blocks in all) CITATION: scripts/dependencies | 6 .ps1/.psm1 files by Glob; with the 8 under tests/scripts/dependencies, CMD-HASHSET enumerates 14 at planning time CITATION: SVGControl/SVGControl.csproj | line 58 Reference Fizzler, Version=1.3.1.0 CITATION: UtilitiesCS/UtilitiesCS.csproj | line 65 Reference Fizzler, Version=1.3.1.0; line 305 Reference System.ClientModel, Version=1.16.0.0 @@ -462,3 +462,5 @@ PREFLIGHT: REVISIONS REQUIRED The PREFLIGHT line above is the hook-required terminator of the bounded record. It records that executor preflight has not yet run; it is not a discovered defect and not a self-approval. The orchestrator runs the validator and the atomic-executor preflight. Revision round 1 (2026-10-02T02-10), applied against the round 1 preflight report and re-derived against the tree in the same pass: section 4 row 4 and P0-T5 (ClientModel 16 blocks, 6 plus 10); CMD-HASHSET and P2-T1 (14 and 16 hash entries); P1-T1 (Import-Module gates replace the `-Force` count); P0-T8(b) (`-o` pattern reads the version; multiline count read per file); C5, CMD-BOM, CMD-JUNIT-DELETE and residual risk 7 (`git -C`, absolute operands, pwsh-refused fallbacks); CMD-EOL, D3, P0-T7 and EDIT-FIZZLER item (3) (space-padded fields, one tab before the path); P2-T7 (`*packages.config`); section 9 and P1-T2 (exact import lines, `$script:` fixtures); P1-T3 and section 9 test 14 (16-entry observed list); P2-T5 (tests 10, 11, 12); section 8 (`[AllowEmptyString()]` and the corrected help sentence, with the note before the fence); P0-T12 and P2-T17 (D8 named as the Coverage Evidence Contract exception). Task count unchanged at 47. + +Revision round 2 (2026-10-02T02-45), applied against the round 2 preflight report and re-derived against the tree in the same pass: CMD-JUNIT-READ (`-n` on every Grep, the Read-tool transcription of the `failure` message, and the long-line rule for any line the Grep tool omits; the planner re-observed the omission on version 1.2 of this plan with Grep pattern `^.{500,}` and read section 9 test 14 in full with the Read tool), with P0-T12, P1-T3, P1-T15 and P2-T3 stating that their JUNIT lines follow the rule; P1-T3 (a test 13 or 14 failure whose message does not begin `Expected ` and contain `, but got ` is a defect in a new file and re-runs as `.iter`); section 7 and section 9 tests 13 and 14 (`before Phase 1` replaced by the P1-T3 position, because tests 13 and 14 are authored at P1-T2 inside Phase 1; no other occurrence of the phrase exists in the plan, checked by Grep); the section 13 CITATION for VBFunctions.Test/app.config (UtilitiesCS 107-108 added to the 1.16.0.0 list; all sixteen System.ClientModel blocks re-derived by Grep `name="System.ClientModel"` with `-A 1`: 1.3.0.0 at QuickFiler 102-103, Tags 102-103, TaskMaster 114-115, TaskTree 102-103, TaskVisualization 102-103, ToDoModel 107-108; 1.16.0.0 at QuickFiler.Test 102-103, SVGControl.Test 46-47, TaskVisualization.Test 102-103, ToDoModel.Test 102-103, UtilitiesCS.Test 122-123, TaskMaster.Test 98-99, TaskTree.Test 222-223, Tags.Test 222-223, UtilitiesCS 107-108, VBFunctions.Test 98-99); P1-T2 (`-Force$` becomes `-Force\r?$` on both import-line gates, because ripgrep's `$` does not match before a carriage return; the only other `$`-anchored gate over a file the executor writes is P0-T3's `^## Acceptance Criteria$` on issue.md, which the planner left unchanged after observing that issue.md carries zero carriage returns and that the pattern matches once; the `\r$` patterns of P0-T7 and section 4 match the carriage return deliberately). Task count unchanged at 47. From 7007db55011fe4a3b5d030407f182a69386daf60 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 02:05:38 -0400 Subject: [PATCH 07/14] docs(953): apply preflight round 3 revisions to the atomic plan --- .../plan.2026-10-02T00-16.md | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index d4d2d7cca..ed3440804 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -4,9 +4,9 @@ - **Parent (optional):** none - **Owner:** drmoisan - **Branch:** bug/stale-fizzler-and-unsafe-binding-redirects-953 -- **Last Updated:** 2026-10-02T02-45 -- **Status:** Draft (awaiting validator and executor preflight; revision rounds 1 and 2 applied) -- **Version:** 1.2 +- **Last Updated:** 2026-10-02T03-20 +- **Status:** Draft (awaiting validator and executor preflight; revision rounds 1 to 3 applied) +- **Version:** 1.3 - **Work Mode:** minor-audit (issue.md line 9) - **Task Count:** 47 (Phase 0: 13, Phase 1: 17, Phase 2: 17), counted mechanically over lines matching the task pattern @@ -91,7 +91,7 @@ Nothing else is written. `artifacts/pester/*` is written by the MCP test tool an - **C2 — Artifact naming.** `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence//-..md`, for example `evidence/baseline/p0-t4-fizzler-census.2026-10-05T09-12.md`. Loop iterations add `.iter` before the timestamp. - **C3 — MCP exit derivation.** For an MCP PoshQC call, `EXIT_CODE:` is 0 when the payload's `ok` is true (and, for the test tool, when the JUnit root `failures` is 0) and 1 otherwise; the payload is recorded verbatim with the worktree root replaced by ``. The tool's own process exit is not observable and is not claimed. - **C4 — Host paths.** No evidence line carries an absolute host path, machine name or account name; the worktree prefix is replaced by ``. -- **C5 — Tool routes.** Read-only tree observations use the Read, Grep and Glob tools or `git`. Grep patterns written in this plan are ripgrep patterns run through the Grep tool with the stated glob. Two PowerShell one-liners (CMD-BOM, CMD-JUNIT-DELETE) contain no double quote and no `$`, and each names its file operand by absolute path. No other PowerShell command is issued. Every git command in this plan, including every one written in task text without the option, is issued as `git -C ...`, so pathspecs and `.` resolve against the worktree root whatever the executor's current directory; every Grep and Glob call passes `` as its path; every file operand of CMD-BOM and CMD-JUNIT-DELETE is absolute (`/`). Evidence records the placeholder, never the host path (C4). If the Bash tool refuses a pwsh invocation, the executor uses the stated fallback and records `PWSH-REFUSED: `; the fallback is not a deviation. +- **C5 — Tool routes.** Read-only tree observations use the Read, Grep and Glob tools or `git`. Grep patterns written in this plan are ripgrep patterns run through the Grep tool with the stated glob. Two PowerShell one-liners (CMD-BOM, CMD-JUNIT-DELETE) contain no double quote and no `$`, and each names its file operand by absolute path. No other PowerShell command is issued. Every git command in this plan, including every one written in task text without the option, is issued as `git -C ...`, so pathspecs and `.` resolve against the worktree root whatever the executor's current directory; every Grep and Glob call passes `` as its path, except a Grep over `artifacts/pester/pester-junit.xml`, which passes the absolute file path `/artifacts/pester/pester-junit.xml` as its path and no glob, because the Grep tool honours `.gitignore` when its path is a directory and `artifacts/` is ignored (`.gitignore` line 57), so a root path with a glob returns no match for that file (observed at preflight round 3); the Glob tool was observed in the same round to return that gitignored file with a worktree root as its path, so the Glob existence and absence checks of CMD-JUNIT-DELETE and CMD-JUNIT-READ keep the root path; every file operand of CMD-BOM and CMD-JUNIT-DELETE is absolute (`/`). Evidence records the placeholder, never the host path (C4). If the Bash tool refuses a pwsh invocation, the executor uses the stated fallback and records `PWSH-REFUSED: `; the fallback is not a deviation. - **C6 — Git gate scoping.** Every `git diff` is anchored at `` (P0-T2). Every footprint evaluation removes paths under `.claude/agent-memory/` and paths listed in the P0-T2 `INHERITED:` capture before comparing against the Write Set. Gitignored paths (`artifacts/`, `coverage/`) never appear in porcelain and need no subtraction. - **C7 — Stop rule.** Where a task says STOP, the executor writes the task's artifact with the observed values, leaves the plan checkbox unchecked, and ends the run with a report to the orchestrator. No stop is a pass. - **C8 — Check-off protocol.** Plan checkboxes are flipped only after the task's artifact exists with every C1 field. issue.md check-offs happen only in P2-T9 to P2-T14, one criterion per task, changing `- [ ]` to `- [x]` and nothing else on the line. @@ -102,11 +102,11 @@ Nothing else is written. `artifacts/pester/*` is written by the MCP test tool an **CMD-POSHQC-ANALYZE** — MCP `mcp__drm-copilot__run_poshqc_analyze` with the same `workspace_root` and `scan_folders`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC analyze against '' with 2 selected scan folder(s).` The payload carries no count, file or rule; `ok` true is the lint result and is recorded with the line `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. `ok` false is a failed step. -**CMD-JUNIT-DELETE** — `pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue'` followed by a Glob for `artifacts/pester/pester-junit.xml` that must return nothing. Run immediately before every CMD-POSHQC-TEST so the document read afterwards was written by that run. The pwsh payload contains no double quote. Fallback when pwsh is refused: `rm -f /artifacts/pester/pester-junit.xml` followed by the same Glob. +**CMD-JUNIT-DELETE** — `pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue'` followed by a Glob for `artifacts/pester/pester-junit.xml` with `` as its path that must return nothing (the Glob tool returns a gitignored file when one exists, observed at preflight round 3 with a worktree root as its path, so an empty result is an absence observation; C5). Run immediately before every CMD-POSHQC-TEST so the document read afterwards was written by that run. The pwsh payload contains no double quote. Fallback when pwsh is refused: `rm -f /artifacts/pester/pester-junit.xml` followed by the same Glob. **CMD-POSHQC-TEST** — MCP `mcp__drm-copilot__run_poshqc_test` with `workspace_root` `` and `scan_folders` `["tests/scripts/dependencies"]`. Success-case payload: `ok` true and a summary reading `Ran bundled PoshQC test against '' with 1 selected scan folder(s).`; a failing run observed on this repository returned `ok` false with summary `Command exited with code 4.` The tool writes `artifacts/pester/pester-junit.xml` (and two coverage documents that instrument none of `scripts/dependencies` and are never read). Every test task records the payload verbatim (C3, C4), then runs CMD-JUNIT-READ, and carries the literal line `COVERAGE-MEASUREMENT: deferred to the CI Pester job (_pester.yml); no local figure`. -**CMD-JUNIT-READ** — Over `artifacts/pester/pester-junit.xml`, which must exist (Glob) after the run: (a) Grep pattern ` failures= errors= disabled=` from that line's attributes; (b) Grep pattern ` tests= failures= skipped=` line per match, the leaf taken after the last path separator so no host path is transcribed; (c) Grep pattern `status="Failed"` with `-n` and `-A 2` → one `JUNIT-NOTPASSED ` line per matched testcase, then for each match the Read tool on the same file with `offset` equal to the matched line number and `limit` 3, from whose output the `JUNIT-MESSAGE ` line is transcribed, with any worktree prefix replaced per C4; when (c) returns no match the artifact states `JUNIT-NOTPASSED: none`. Long-line rule: the Grep tool replaces an output line longer than about 500 characters with `[Omitted long matching line]` or `[Omitted long context line]` (observed at preflight round 2 on four lines of version 1.1 of this plan, and re-observed by the planner on version 1.2, where Grep pattern `^.{500,}` over this plan reports every line over 500 characters as omitted, among them the CMD-POSHQC-FORMAT, CMD-JUNIT-DELETE and CMD-JUNIT-READ definitions and section 9 tests 13 and 14); a `testsuite` line carries the absolute test-file path twice, and the `failure` lines of tests 13 and 14 are estimated at about 550 and 1,700 characters. Whenever a Grep in (a), (b) or (c) reports an omitted line, the executor reads that line number with the Read tool (`offset` equal to the line number, `limit` 1) and transcribes from the Read output; the Read tool returned section 9 test 14 of this plan (one line of about 3,000 characters) in full at preflight round 2 and again to the planner in this revision pass. Every task that transcribes JUNIT lines (P0-T12, P1-T3, P1-T15, P2-T3) follows this rule. Expected suite leaf names at planning time: AnalyzerItemRepair.Tests.ps1, ConsistencyVerifier.Tests.ps1, DependabotConfig.Tests.ps1, PackageCompatibility.Tests.ps1, PackageGraph.Tests.ps1, ProjectConsistency.Tests.ps1, Repair-PackageManifestConsistency.Tests.ps1, RepositoryTreeConsistency.Tests.ps1 and, from P1-T2, BindingRedirectVerification.Tests.ps1. Suite counts are recorded as observed at P0-T12 and compared against that record later; this plan pins no pre-existing suite's count. +**CMD-JUNIT-READ** — Over `artifacts/pester/pester-junit.xml`, which must exist (Glob) after the run, every Grep below passing the absolute file path `/artifacts/pester/pester-junit.xml` as its path and no glob (C5 exception): (a) Grep pattern ` failures= errors= disabled=` from that line's attributes; (b) Grep pattern ` tests= failures= skipped=` line per match, the leaf taken after the last path separator so no host path is transcribed; (c) Grep pattern `status="Failed"` with `-n` and `-A 2` → one `JUNIT-NOTPASSED ` line per matched testcase, then for each match the Read tool on the same file with `offset` equal to the matched line number and `limit` 3, from whose output the `JUNIT-MESSAGE ` line is transcribed, with any worktree prefix replaced per C4; when (c) returns no match and (a) matched exactly one line in the same task, the artifact states `JUNIT-NOTPASSED: none`; a no-match from (a) is a failed read, not an empty result. Long-line rule: the Grep tool replaces an output line longer than about 500 characters with `[Omitted long matching line]` or `[Omitted long context line]` (observed at preflight round 2 on four lines of version 1.1 of this plan, and re-observed by the planner on versions 1.2 and 1.3, where Grep pattern `^.{500,}` over this plan reports every line over 500 characters as omitted, among them the CMD-POSHQC-FORMAT, CMD-JUNIT-DELETE and CMD-JUNIT-READ definitions and section 9 tests 13 and 14); a `testsuite` line carries the absolute test-file path twice, so whether it is omitted depends on the execution worktree path length: at the `.claude/worktrees/agent-` depth a dependencies testsuite line measured 403 to 439 characters at preflight round 3, below the 501-character omission threshold, and the Grep tool printed it in full; the test 13 `failure` message attribute is about 333 characters by construction (eleven `=1.3.0.0` entries) and is expected to print in full, and the test 14 `failure` line, which lists 16 pairs three times, is expected to exceed the threshold; neither failure line has been observed. Whenever a Grep in (a), (b) or (c) reports an omitted line, the executor reads that line number with the Read tool (`offset` equal to the line number, `limit` 1) and transcribes from the Read output; the Read tool returned section 9 test 14 of this plan (one line of 2,000 to 2,099 characters at version 1.3: Grep `^.{2000,}` matches it and `^.{2100,}` does not) in full at preflight round 2 and again to the planner in the round 2 and round 3 revision passes. Every task that transcribes JUNIT lines (P0-T12, P1-T3, P1-T15, P2-T3) follows this rule. Expected suite leaf names at planning time: AnalyzerItemRepair.Tests.ps1, ConsistencyVerifier.Tests.ps1, DependabotConfig.Tests.ps1, PackageCompatibility.Tests.ps1, PackageGraph.Tests.ps1, ProjectConsistency.Tests.ps1, Repair-PackageManifestConsistency.Tests.ps1, RepositoryTreeConsistency.Tests.ps1 and, from P1-T2, BindingRedirectVerification.Tests.ps1. Suite counts are recorded as observed at P0-T12 and compared against that record later; this plan pins no pre-existing suite's count. **CMD-EOL ``** — `git ls-files --eol -- `. Output is one line of the form `i/ w/ attr/` separated by runs of spaces, then one tab and `` (observed: `i/lf w/crlf attr/text=auto` then a tab and the path). The gate reads the second whitespace-separated field, which describes the working-tree file's terminators: `w/crlf` passes; `w/lf`, `w/mixed`, `w/none` or `w/-text` fails. The first field is recorded and not gated. @@ -349,14 +349,14 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on - [ ] [P0-T9] Baseline line counts (CMD-LINECOUNT) for `scripts/dependencies/PackageGraph.psm1` (465), `scripts/dependencies/ProjectConsistency.psm1` (381), `scripts/dependencies/ConsistencyVerifier.psm1` (499), `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152), and Glob confirmation that `scripts/dependencies/BindingRedirectVerification.psm1` and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` do not yet exist. Acceptance: the four counts equal the figures in parentheses (a difference is recorded as `LINECOUNT-DRIFT:` and the run continues, because none of the four is edited) and both Globs return nothing (a hit is STOP: PREEXISTING-FILE). Artifact `evidence/baseline/p0-t9-linecount-baseline..md`. - [ ] [P0-T10] Baseline PowerShell format step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, then CMD-POSHQC-FORMAT, then CMD-HASHSET again and `git status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies`. Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical and the porcelain over the two folders is empty (the Write Set is clean per P0-T2 and the new files do not exist yet). If a hash differs, the formatter rewrote pre-existing drift outside this item's change: record the paths as `FORMAT-DRIFT-REVERTED:`, restore each with `git checkout -- `, re-run CMD-HASHSET to confirm equality with the first set, and continue. `EXIT_CODE:` per C3. Artifact `evidence/baseline/p0-t10-poshqc-format..md`. - [ ] [P0-T11] Baseline PowerShell analyze step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance: `ok` true with the 2-folder summary literal (the folders were analyzer-clean on 2026-09-30 per issue 929 evidence); `ok` false is STOP: ANALYZE-BASELINE-RED, because pre-existing analyzer debt in the scan scope is outside this plan and would make P2-T2 unsatisfiable. Record `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. Artifact `evidence/baseline/p0-t11-poshqc-analyze..md`. -- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines are transcribed under the CMD-JUNIT-READ long-line rule: any line the Grep tool reports as omitted is read by line number with the Read tool, which is expected for every `testsuite` line because each carries the absolute test-file path twice). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. +- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines are transcribed under the CMD-JUNIT-READ long-line rule: any line the Grep tool reports as omitted is read by line number with the Read tool; whether a testsuite line is omitted depends on the execution worktree path length: at the .claude/worktrees/agent- depth a dependencies testsuite line measured 403 to 439 characters at preflight round 3, below the 501-character omission threshold). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. - [ ] [P0-T13] Record the PowerShell budget baseline from `.claude/hooks/enforce-powershell-batch-budget.ps1`: quote lines 10-11 (caps), 14 (state file location) and 42-45 (deny behaviour); Glob `.claude/state/powershell-batch-budget.*.json` and record whether any state file exists (read-only; never opened for writing); restate the D9 stop rule in the artifact. Acceptance: the artifact quotes the three regions and carries the line `BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable`. Artifact `evidence/baseline/p0-t13-budget-baseline..md`. ### Phase 1 — Implementation: detector module, regression tests, Fizzler sweep - [ ] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `^Import-Module ` count 1 and Grep pattern `^Import-Module .*Force` count 0 (the import statement carries no -Force; the explanatory comment at the top of the module mentions -Force and is not gated). A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. - [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/PackageGraph\.psm1'\) -Force\r?$` count 1 and Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/BindingRedirectVerification\.psm1'\) -Force\r?$` count 1 (the two section 9 import lines; the `\r?` admits a carriage return before the line end, because ripgrep's `$` does not match before a carriage return and the terminator the Write tool and the PoshQC formatter leave on this new file is not pinned by this plan; the gate asserts the line content, not the terminator), Grep pattern `^\s*Import-Module ` count 2, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. -- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (the two JUNIT-MESSAGE lines below are transcribed from the Read tool under the CMD-JUNIT-READ long-line rule, because both `failure` lines are expected to exceed the length at which the Grep tool omits a line; the Grep `-n` output supplies the line numbers the Read tool is pointed at). Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12, or a failure of test 13 or 14 whose JUNIT-MESSAGE does not begin `Expected ` and contain `, but got ` (an exception or StrictMode error inside the test body rather than the expected assertion failure), is a defect in a new file: fix the file concerned, record the correction, and re-run this task as `.iter`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. +- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (the two JUNIT-MESSAGE lines below are transcribed from the Read tool under the CMD-JUNIT-READ long-line rule as CMD-JUNIT-READ (c) requires for every not-passed testcase, whatever the line length; the Grep `-n` output supplies the line numbers the Read tool is pointed at). Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12, or a failure of test 13 or 14 whose JUNIT-MESSAGE does not begin `Expected ` and contain `, but got ` (an exception or StrictMode error inside the test body rather than the expected assertion failure), is a defect in a new file: fix the file concerned, record the correction, and re-run this task as `.iter`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. - [ ] [P1-T4] EDIT-FIZZLER `QuickFiler/app.config` lines 50-51; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t4-quickfiler-redirect..md`. - [ ] [P1-T5] EDIT-FIZZLER `QuickFiler.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t5-quickfiler-test-redirect..md`. - [ ] [P1-T6] EDIT-FIZZLER `SVGControl.Test/app.config` lines 18-19; System.ClientModel at 46-47 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t6-svgcontrol-test-redirect..md`. @@ -368,7 +368,7 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on - [ ] [P1-T12] EDIT-FIZZLER `ToDoModel/app.config` lines 51-52; System.ClientModel at 107-108 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t12-todomodel-redirect..md`. - [ ] [P1-T13] EDIT-FIZZLER `ToDoModel.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t13-todomodel-test-redirect..md`. - [ ] [P1-T14] EDIT-FIZZLER `UtilitiesCS.Test/app.config` lines 46-47; System.ClientModel at 122-123 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t14-utilitiescs-test-redirect..md`. -- [ ] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. +- [ ] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. - [ ] [P1-T16] Sweep verification over `*/app.config` (AC1 evidence): Grep pattern `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` glob `*/app.config` count mode → 13 occurrences across 13 files; Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → exactly 6 occurrences across exactly QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; Grep pattern `name="Fizzler"` with `-A 1` → 13 blocks all followed by `1.3.1.0` in both attributes; `git diff --name-only -- '*/app.config'` lists exactly the 11 Write Set config paths, paired with `git status --porcelain -- '*/app.config'` showing exactly 11 ` M` lines for the same paths. Acceptance: all four observations hold. Artifact `evidence/qa-gates/p1-t16-sweep-verification..md`. - [ ] [P1-T17] Unsafe re-verification after the edits over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1` → 17 blocks all `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; `git diff -- '*/app.config'` contains no content line (beginning `-` or `+` after the header pairs) that contains `Unsafe`, and its 22 content lines are the 11 removed and 11 added Fizzler redirect lines. Acceptance: both hold. Artifact `evidence/qa-gates/p1-t17-unsafe-unchanged..md`. @@ -378,7 +378,7 @@ The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 retu - [ ] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (16 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. - [ ] [P2-T2] Final QC analyze step (toolchain step 2) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal and the `GATE-SUBSTITUTION:` line. `ok` false: fix the new files only and restart at P2-T1. Type checking is not applicable to PowerShell (`.claude/rules/powershell.md` line 17) and is recorded as such in the artifact. Artifact `evidence/qa-gates/p2-t2-poshqc-analyze.iter..md`. -- [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none`; the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. +- [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. - [ ] [P2-T4] Loop closure record for the QC toolchain under `evidence/qa-gates/`: list every iteration run (P2-T1, P2-T2, P2-T3 artifact names) and name the terminal iteration N on which P2-T1 recorded `REWRITE-COUNT: 0`, P2-T2 `ok` true and P2-T3 `failures=0` together. Acceptance: the three terminal-iteration artifacts exist (Glob) and carry those values. Artifact `evidence/qa-gates/p2-t4-loop-closure..md`. - [ ] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10), negative (11) and edge (12, one assembly at two versions across several project texts) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. - [ ] [P2-T6] File-size audit over `scripts/dependencies/` and `tests/scripts/dependencies/` after the terminal format pass: CMD-LINECOUNT for the two new files and the four P0-T9 neighbours. Acceptance: `BindingRedirectVerification.psm1` below 500 (record the value with `TARGET-BAND: 100-150` and whether it lies inside), `BindingRedirectVerification.Tests.ps1` below 500, and the four neighbour counts equal their P0-T9 values (they are not edited). Markdown under the feature folder is exempt from the 500-line cap per `.claude/rules/general-code-change.md`. Artifact `evidence/qa-gates/p2-t6-file-size-audit..md`. @@ -464,3 +464,5 @@ The PREFLIGHT line above is the hook-required terminator of the bounded record. Revision round 1 (2026-10-02T02-10), applied against the round 1 preflight report and re-derived against the tree in the same pass: section 4 row 4 and P0-T5 (ClientModel 16 blocks, 6 plus 10); CMD-HASHSET and P2-T1 (14 and 16 hash entries); P1-T1 (Import-Module gates replace the `-Force` count); P0-T8(b) (`-o` pattern reads the version; multiline count read per file); C5, CMD-BOM, CMD-JUNIT-DELETE and residual risk 7 (`git -C`, absolute operands, pwsh-refused fallbacks); CMD-EOL, D3, P0-T7 and EDIT-FIZZLER item (3) (space-padded fields, one tab before the path); P2-T7 (`*packages.config`); section 9 and P1-T2 (exact import lines, `$script:` fixtures); P1-T3 and section 9 test 14 (16-entry observed list); P2-T5 (tests 10, 11, 12); section 8 (`[AllowEmptyString()]` and the corrected help sentence, with the note before the fence); P0-T12 and P2-T17 (D8 named as the Coverage Evidence Contract exception). Task count unchanged at 47. Revision round 2 (2026-10-02T02-45), applied against the round 2 preflight report and re-derived against the tree in the same pass: CMD-JUNIT-READ (`-n` on every Grep, the Read-tool transcription of the `failure` message, and the long-line rule for any line the Grep tool omits; the planner re-observed the omission on version 1.2 of this plan with Grep pattern `^.{500,}` and read section 9 test 14 in full with the Read tool), with P0-T12, P1-T3, P1-T15 and P2-T3 stating that their JUNIT lines follow the rule; P1-T3 (a test 13 or 14 failure whose message does not begin `Expected ` and contain `, but got ` is a defect in a new file and re-runs as `.iter`); section 7 and section 9 tests 13 and 14 (`before Phase 1` replaced by the P1-T3 position, because tests 13 and 14 are authored at P1-T2 inside Phase 1; no other occurrence of the phrase exists in the plan, checked by Grep); the section 13 CITATION for VBFunctions.Test/app.config (UtilitiesCS 107-108 added to the 1.16.0.0 list; all sixteen System.ClientModel blocks re-derived by Grep `name="System.ClientModel"` with `-A 1`: 1.3.0.0 at QuickFiler 102-103, Tags 102-103, TaskMaster 114-115, TaskTree 102-103, TaskVisualization 102-103, ToDoModel 107-108; 1.16.0.0 at QuickFiler.Test 102-103, SVGControl.Test 46-47, TaskVisualization.Test 102-103, ToDoModel.Test 102-103, UtilitiesCS.Test 122-123, TaskMaster.Test 98-99, TaskTree.Test 222-223, Tags.Test 222-223, UtilitiesCS 107-108, VBFunctions.Test 98-99); P1-T2 (`-Force$` becomes `-Force\r?$` on both import-line gates, because ripgrep's `$` does not match before a carriage return; the only other `$`-anchored gate over a file the executor writes is P0-T3's `^## Acceptance Criteria$` on issue.md, which the planner left unchanged after observing that issue.md carries zero carriage returns and that the pattern matches once; the `\r$` patterns of P0-T7 and section 4 match the carriage return deliberately). Task count unchanged at 47. + +Revision round 3 (2026-10-02T03-20), applied against the round 3 preflight report and re-derived against the tree in the same pass: C5 and CMD-JUNIT-READ (every Grep over `artifacts/pester/pester-junit.xml` passes the absolute file path and no glob, because the Grep tool honours `.gitignore` when its path is a directory and `artifacts/` is ignored at `.gitignore` line 57; the planner reproduced both outcomes in a sibling worktree at the same `.claude/worktrees/agent-` depth, where Grep pattern `testsuites` with the worktree root as path and the glob `artifacts/pester/pester-junit.xml` returned no match and the same pattern with the absolute file path returned the ` Date: Fri, 2 Oct 2026 02:15:26 -0400 Subject: [PATCH 08/14] docs(953): record preflight clearance for the atomic plan --- .../preflight-clearance.2026-10-02T03-45.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/preflight-clearance.2026-10-02T03-45.md diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/preflight-clearance.2026-10-02T03-45.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/preflight-clearance.2026-10-02T03-45.md new file mode 100644 index 000000000..9a3a9abbb --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/preflight-clearance.2026-10-02T03-45.md @@ -0,0 +1,31 @@ +# Preflight clearance for the atomic plan (issue 953) + +Timestamp: 2026-10-02T03-45 +Command: atomic-executor with DIRECTIVE: PREFLIGHT VALIDATION ONLY, run four times against the plan path below +EXIT_CODE: 0 +Output Summary: The fourth preflight round returned no defects and cleared the plan. + +- Plan path: docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +- Plan version cleared: 1.3 +- Plan blob SHA cleared (git rev-parse of the committed plan at the clearing head): ed3440804d29e0b936fbfa54073fd6a4d93a6759 +- Clearing head commit: 7007db550 +- Work mode: minor-audit +- Plan validator (plan artifact type): ok, no errors, before and after each revision round + +PREFLIGHT: ALL CLEAR +CONVERGENCE: NO FURTHER ROUNDS EXPECTED + +Rounds: 4 + +| Round | Plan version reviewed | Signal | Defects | Convergence line | +|---|---|---|---|---| +| 1 | 1.0 | PREFLIGHT: REVISIONS REQUIRED | 11 plus 1 advisory | NO FURTHER ROUNDS EXPECTED | +| 2 | 1.1 | PREFLIGHT: REVISIONS REQUIRED | 4 plus 1 advisory | NO FURTHER ROUNDS EXPECTED | +| 3 | 1.2 | PREFLIGHT: REVISIONS REQUIRED | 4 | FURTHER ROUNDS LIKELY | +| 4 | 1.3 | PREFLIGHT: ALL CLEAR | 0 | NO FURTHER ROUNDS EXPECTED | + +Defect classes found across rounds 1 to 3: a miscounted tree figure (System.ClientModel blocks), an unsatisfiable Grep count gate, a Grep pattern that could not print the asserted value, a Grep tool limit on long lines and on gitignored directory paths that would have blocked the failing-test transcription, command forms that depend on the executor working directory and on pwsh availability, and wording that overstated a prediction. + +Scope of the cleared plan: sweep 11 stale Fizzler bindingRedirect lines to 1.3.1.0; add the BindingRedirectVerification module and its Pester tests with a ratchet over the recorded known-debt set; no C# change. + +Not observed by any reviewer (carried as residual risks in the plan): execution of the PoshQC tools, the Pester failure-message text for tests 13 and 14, pwsh behaviour under the executor session, and the PowerShell file budget state at execution time. From 8a3e7667a2bbaafb0865ef8676e6f244711814b4 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 03:06:51 -0400 Subject: [PATCH 09/14] docs(memory): commit planner agent memory from the 953 preparation passes Co-Authored-By: Claude Sonnet 5.5 --- .claude/agent-memory/atomic-planner/MEMORY.md | 4 +++ ...r_redirect_sweep_and_ratchet_plan_seams.md | 23 ++++++++++++++++ ...r1_pwsh_refused_and_count_recheck_seams.md | 27 +++++++++++++++++++ ..._long_line_omission_and_cr_anchor_seams.md | 21 +++++++++++++++ ...irectory_path_and_measured_line_lengths.md | 21 +++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 .claude/agent-memory/atomic-planner/project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md create mode 100644 .claude/agent-memory/atomic-planner/project_953_r1_pwsh_refused_and_count_recheck_seams.md create mode 100644 .claude/agent-memory/atomic-planner/project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md create mode 100644 .claude/agent-memory/atomic-planner/project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md diff --git a/.claude/agent-memory/atomic-planner/MEMORY.md b/.claude/agent-memory/atomic-planner/MEMORY.md index dd86b9a95..3795943e2 100644 --- a/.claude/agent-memory/atomic-planner/MEMORY.md +++ b/.claude/agent-memory/atomic-planner/MEMORY.md @@ -2,6 +2,10 @@ ## Preflight revision seams (per issue; newest first) +- [#953 R3](project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md) — Grep honours .gitignore for a DIRECTORY path (root+glob over artifacts/ returns nothing; pass the absolute file path), Glob does not; a `none` outcome needs its positive control in the same task; measure line lengths with `^.{N,}` probes, never estimate; leading `<` in a pattern works +- [#953 R2](project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md) — Grep tool omits lines over ~500 chars (JUnit read needs `-n` + Read offset/limit); ripgrep `$` fails before CR (`\r?$` on executor-written files); "before Phase 1" wrong when tests are authored in P1 (anchor to task ids); expect-fail needs a wrong-reason branch keyed to `Expected `/`, but got ` +- [#953 R1](project_953_r1_pwsh_refused_and_count_recheck_seams.md) — Bash guard refuses pwsh (od/rm fallbacks, `git -C `, absolute operands); re-count `-A 1` blocks per file (16 not 17); Glob hash-set folders (14 not 13); `-Force` count-0 self-hits the module comment; `-o` must extend past `Version=`; `'*packages.config'` pathspec; `[AllowEmptyString()]` on Mandatory `[string[]]`; ls-files --eol fields space-padded +- [#953 R0](project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md) — ripgrep strips the BOM (zero-hit Grep inconclusive); text=auto hides CRLF loss from git diff (gate `git ls-files --eol` w/ column); shared attribute string on a sibling block needs a two-line Edit; banned double quotes push the JUnit read onto Read/Grep with delete-before freshness - [#956 R1](project_956_r1_spec_self_hit_and_raw_doc_name_seams.md) — spec-wording counts self-hit the AC line; `cobertura` substring matches tracked agent-memory .md (anchor `cobertura[^/]*\.xml$`); orchestrator-amended AC needs a P0 literal check - [#945 R0](project_945_sortemail_trysave_directory_seam_plan_seams.md) — whitespace-stripped token census; compile-red fail-before (runtime-red would create a real dir); uncommitted-fix control via backup copy; no-commit plan, two-dot MERGE-BASE gates diff --git a/.claude/agent-memory/atomic-planner/project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md b/.claude/agent-memory/atomic-planner/project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md new file mode 100644 index 000000000..a0bdebbbe --- /dev/null +++ b/.claude/agent-memory/atomic-planner/project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md @@ -0,0 +1,23 @@ +--- +name: project-953-fizzler-redirect-sweep-and-ratchet-plan-seams +description: "#953 R0 minimal-audit plan seams - ripgrep strips a UTF-8 BOM so a Grep for BOM bytes is inconclusive; .gitattributes text=auto hides a CRLF-to-LF rewrite from git diff (use git ls-files --eol w/ column); a shared attribute string on a sibling block defeats single-line replace; directive-banned double quotes in pwsh -Command pushed the JUnit read onto Read/Grep tools with delete-before freshness; multiline Grep name+newVersion count re-measures a known-debt table in one call per row" +metadata: + type: project +--- + +Seams re-derived while authoring the issue #953 plan (Fizzler redirect sweep plus a Pester ratchet detector) in worktree `.claude/worktrees/agent-a5292122c820774d3` on 2026-10-02, revision round 0. 47 tasks, 3 phases, no commits. + +**Why:** each one either made a caller-supplied verification instruction unobservable with the tools available, or would have drawn a preflight finding. + +**How to apply:** re-check before planning any app.config or other CRLF-and-BOM file edit, any PowerShell plan where the delegation bans double quotes in `pwsh -Command`, or any ratchet test keyed to a measured table. + +1. **ripgrep strips a UTF-8 BOM before matching.** Grep patterns `^\x{FEFF}` and `(?-u)^\xEF\xBB\xBF` both returned zero matches over 17 files the orchestrator had verified carry a BOM. A zero-hit Grep for the BOM is therefore inconclusive, never evidence of absence. Observe a BOM with `git diff --numstat -- ` (a lost BOM alters line 1, so a one-line edit reads `2 2` instead of `1 1`) or with a byte read: `pwsh -NoProfile -Command '(Get-Content -LiteralPath -AsByteStream -TotalCount 3) -join [char]44'` prints `239,187,191` and contains no double quote. +2. **`.gitattributes` `* text=auto` makes a CRLF-to-LF rewrite invisible to `git diff`.** git normalises the working-tree text before comparing, so numstat `1 1` does not prove terminators survived. The working-tree observation is `git ls-files --eol -- `: gate the second field (`w/crlf` passes; `w/lf`, `w/mixed` fail) and record the `i/` field without gating it, because whether the index blob was normalised depends on when the file was first committed. +3. **A shared attribute string on a sibling block defeats a single-line replace.** `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` sits on the Fizzler block in 11 configs and also on the System.ClientModel block in six of them (17 occurrences). The Edit tool needs a unique old_string, so include the preceding `assemblyIdentity name="..."` line in a two-line old_string, and verify afterwards that the sibling block is unchanged by a scoped `-A 1` Grep on the sibling name. +4. **When the delegation bans double quotes inside `pwsh -Command`, do not fight the quoting.** The repo's proven `CMD-JUNIT-READ` one-liner (issue 929 plan) needs inner double quotes. Replace it with tool reads over `artifacts/pester/pester-junit.xml`: Grep `` over every PowerShell file in the scan folders before and after `run_poshqc_format` (works on untracked files) and gate on identical hash sets. +6. **Re-measure a known-debt table with one multiline Grep per row.** Pattern `name=""[^\n]*\n[^\n]*newVersion=""` in count mode with `multiline: true` returns the per-file hit list for that (assembly, version) pair in one call; pair it with a `-o` Grep of `Include=", Version=` over the csproj set for the Reference side. The 15 rows for #953 re-derived to 137 entries; `; a -A 1 block count must be re-derived per file (one app.config carried no block); Glob counts over two folders must be re-run not recalled; a count-0 gate on a token the module's own comment repeats cannot pass; -o on a pattern ending at Version= prints no version; 'packages.config' pathspec is root-only; Mandatory [string[]] needs [AllowEmptyString()] for empty elements; git ls-files --eol fields are space-padded with one tab before the path" +metadata: + type: project +--- + +Round 1 preflight deltas on the issue #953 plan (worktree `.claude/worktrees/agent-a5292122c820774d3`, 2026-10-02). Eleven defects, no task-count change (47). Each one was a figure or command shape I carried forward from the orchestrator's directive or from reasoning instead of observing it in this worktree. + +**Why:** every delta was detectable before handoff by one Grep, one Glob, or one read of the tool's actual output. + +**How to apply:** before any handoff, re-run every census count the plan asserts (per file, not from the directive), and for every shell command either observe its success-case output or write the fallback the executor will need when the Bash tool refuses the binary. + +1. **pwsh is refused by the Bash worktree-isolation guard, in one-liner form too.** `od -A n -t u1 -N 3 ` prints ` 239 187 191` for a BOM file, `rm -f ` exits 0, `git -C ...` runs. Write the fallback into the CMD definition, make every file operand absolute (`/`), state in C5 that every `git` is `git -C `, and have the executor record `PWSH-REFUSED: ` so the fallback is not a deviation. +2. **A `-A 1` block census must be counted per file.** `name="System.ClientModel"` over `*/app.config` gives 16 blocks (6 at 1.3.0.0, 10 at 1.16.0.0); SVGControl/app.config (23 lines) carries none. I had written 17/11 by assuming every one of the 17 configs carried the block. The shared-string count (17 = 11 Fizzler + 6 ClientModel) was right and is a different figure; search the plan for both when correcting one. +3. **Glob the hash-set folders; do not recall the count.** `scripts/dependencies` has 6 `.ps1/.psm1` and `tests/scripts/dependencies` 8 (14, 16 after two new files); I wrote 13/15. +4. **A count-0 gate on `-Force` fails on the module's own comment** that explains why `-Force` is omitted. Gate the statement line instead: `^Import-Module ` count 1 and `^Import-Module .*Force` count 0. +5. **`-o` on a pattern that ends at `Version=` prints no version.** Extend the pattern through the value (`Version=[^,"]+`) so the printed match carries the figure the gate asserts. Multiline count mode reports one count per file: assert "N files, each count 1" rather than a bare total. +6. **`git ls-files --eol` output is `i/lf w/crlf attr/text=autopath`** - the three fields are space-padded, only the path is tab-preceded. Describe the gate as "second whitespace-separated field", not tab fields. +7. **Pathspec `'packages.config'` matches only the repository root.** Use `'*packages.config'` (git pathspec `*` crosses `/`). +8. **Mandatory `[string[]]` rejects an empty element at binding** unless `[AllowEmptyString()]` is present, so help text claiming "the parser rejects empty text" is false without it. Add the attribute and state the real two-step behaviour; cite the parser's own test (PackageGraph.Tests.ps1 line 268) instead of adding an It that would shift every test-count gate. +9. **Pin the test-file import form.** The repo form is `Import-Module (Join-Path $script:RepoRoot '') -Force` (RepositoryTreeConsistency.Tests.ps1 line 5); quote the exact two lines in the spec and gate them with anchored patterns, and require BeforeAll fixtures to be `$script:` variables. +10. **Ratchet drift must be detectable at fail-before, not after all edits.** Pester renders `Should -Be` failures as `Expected ..., because , but got ...`, so a `-Because` that appends `observed: ` lets the fail-before task gate the exact 16-entry list between `observed: ` and `, but got`. +11. **A repository-level ratchet test is not an edge test for the helper it calls.** Map each exported function to its own It numbers (10 positive, 11 negative, 12 edge), not to the integration test. +12. **Coverage Evidence Contract exception must be named where the figures would sit.** When no local coverage route instruments the folder, cite the design decision (D8) in the baseline test task and the handoff task with a literal line stating the exception and the reason. + +Related: [[project-953-fizzler-redirect-sweep-and-ratchet-plan-seams]] (round 0 seams), [[project_pwsh_refused_in_isolated_worktree_agents]], [[powershell-gate-observables]], [[verify-line-spans-and-computed-literals]]. diff --git a/.claude/agent-memory/atomic-planner/project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md b/.claude/agent-memory/atomic-planner/project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md new file mode 100644 index 000000000..03d6d5f32 --- /dev/null +++ b/.claude/agent-memory/atomic-planner/project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md @@ -0,0 +1,21 @@ +--- +name: project-953-r2-grep-long-line-omission-and-cr-anchor-seams +description: "#953 R2 preflight deltas - the Grep tool replaces any output line over about 500 characters with [Omitted long matching line], so a JUnit read via Grep -A 2 cannot transcribe Pester failure messages (use -n for the line number, then Read with offset/limit; Read returns a 3,000-character line in full); ripgrep's $ does not match before a carriage return so end-of-line gates over executor-written files need \\r?$; 'before Phase 1' is wrong when the test is authored inside Phase 1 (anchor to task ids); an expect-fail task needs a wrong-reason branch keyed to the message shape" +metadata: + type: project +--- + +Round 2 preflight deltas on the issue #953 plan (worktree `.claude/worktrees/agent-a5292122c820774d3`, 2026-10-02). Four defects plus one advisory, no task-count change (47). + +**Why:** each one is a tool-behaviour or wording fact that reading the plan could not reveal; the reviewer found them by running the Grep tool over the plan itself and by tracing where the tests are authored. + +**How to apply:** before any plan that transcribes figures from a long-line XML document (JUnit, TRX, Cobertura) with the Grep tool, and before any `$`-anchored gate over a file the executor writes. + +1. **The Grep tool omits long lines.** Any output line longer than about 500 characters is replaced with `[Omitted long matching line]` (match) or `[Omitted long context line]` (`-A`/`-B` context). The line number from `-n` survives. A Pester JUnit `testsuite` line carries the absolute test-file path twice and a `failure` line carrying a `-Because` list runs to 1,700 characters, so a Grep-only CMD-JUNIT-READ cannot transcribe them. Pattern: Grep with `-n` for the line number, then the Read tool with `offset` equal to that line number and `limit` 1 (or 3 to span testcase plus failure child); the Read tool returned a 3,000-character plan line in full. Observe it with Grep pattern `^.{500,}` over any file. +2. **ripgrep `$` does not match before a carriage return.** A gate such as `... -Force$` fails on a CRLF file even when the content is right. On a file the executor writes (new .psm1/.Tests.ps1) whose terminator the plan does not pin, write `\r?$`. Check tracked targets instead of changing them blindly: issue.md was LF (Grep `\r$` count 0) so its `^## Acceptance Criteria$` gate was left alone and the check recorded. +3. **Name the task position, not the phase, for fail-before/pass-after prose.** "red before Phase 1" was false because the regression tests are authored at P1-T2 inside Phase 1; the accurate form is "red at P1-T3, before the config edits P1-T4 to P1-T14, and green at P1-T15". Grep the whole plan for the phrase; it sat in section 7 and twice in section 9. +4. **An expect-fail task needs a wrong-reason branch.** A test that fails with an exception or StrictMode error instead of the expected assertion still yields `failures=1`. Gate the message shape (Pester `Should` failures begin `Expected ` and contain `, but got `) and route anything else to "defect in a new file, fix and re-run as `.iter`". +5. **A parenthetical list in a CITATION is itself a count claim.** The VBFunctions.Test citation listed the other 1.16.0.0 ClientModel configs and omitted UtilitiesCS 107-108 while claiming "10 blocks"; re-enumerate every member when the line is touched. +6. **Plan self-references to line numbers go stale.** When citing the long-line observation, name the CMD definitions and section-9 tests the lines hold, plus the Grep that reproduces it, rather than only the line numbers. + +Related: [[project-953-r1-pwsh-refused-and-count-recheck-seams]], [[project-953-fizzler-redirect-sweep-and-ratchet-plan-seams]], [[powershell-gate-observables]], [[verify-line-spans-and-computed-literals]]. diff --git a/.claude/agent-memory/atomic-planner/project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md b/.claude/agent-memory/atomic-planner/project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md new file mode 100644 index 000000000..31d25917d --- /dev/null +++ b/.claude/agent-memory/atomic-planner/project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md @@ -0,0 +1,21 @@ +--- +name: project-953-r3-grep-gitignore-directory-path-and-measured-line-lengths +description: "#953 R3 preflight deltas - the Grep tool honours .gitignore when its path is a DIRECTORY (root path + glob artifacts/pester/pester-junit.xml returns no match because artifacts/ is ignored) but matches when given the absolute FILE path; the Glob tool returns the gitignored file either way; a 'none' outcome needs its positive control (root line matched exactly once); line-length predictions (testsuite lines, failure messages, plan lines) must be measured with ^.{N,} probes, not estimated; a leading < in a Grep pattern works when passed as written" +metadata: + type: project +--- + +Round 3 preflight deltas on the issue #953 plan (worktree `.claude/worktrees/agent-a5292122c820774d3`, 2026-10-02). Four defects, no task-count change (47), version 1.2 to 1.3. + +**Why:** every one is a tool-behaviour fact that reading the plan could not reveal; the reviewer found them by running the Grep tool over a sibling worktree's JUnit document and over the plan itself. The planner reproduced each one before editing. + +**How to apply:** before any plan whose executor reads a gitignored document (JUnit, TRX, Cobertura under `artifacts/` or `coverage/`) with the Grep tool, and before any plan that states a line length or an omission prediction. + +1. **Grep honours .gitignore for a directory path, Glob does not.** Grep with the worktree root as `path` plus a `glob` naming a gitignored file returns `No matches found`; the same pattern with the absolute file path as `path` returns the match. Glob with the worktree root as `path` returned the same gitignored file. Rule written into C5: a Grep over `artifacts/pester/pester-junit.xml` passes the absolute file path and no glob; Glob existence/absence checks keep the root path. A pattern that depends on this (every CMD-JUNIT-READ Grep) would otherwise record nothing and the root-count-derived `EXIT_CODE` (C3) would have no source. +2. **A `none` outcome needs a positive control in the same task.** `JUNIT-NOTPASSED: none` is valid only when the `` depth with `^.{390,}` / `^.{440,}` it is 390-439 characters and prints in full. The plan said test 14 was "about 3,000 characters"; `^.{2000,}` matches and `^.{2100,}` does not. A failure-message length can be computed from its construction (test 13: 72 + 140 + 88 + 20 + 13 = 333 characters) when it cannot be observed. Write the probe patterns into the plan so the figure is reproducible, and bump the version the figure is pinned to when the revision changes the file. +4. **Do not justify a tool choice by an unobserved property.** P1-T3 said the Read tool is used "because both failure lines are expected to exceed" the omission length; the definition already requires the Read tool for every not-passed testcase whatever the length. Cite the rule, not a prediction. +5. **A leading `<` in a Grep pattern works.** ` Date: Fri, 2 Oct 2026 03:17:15 -0400 Subject: [PATCH 10/14] docs(953): record Phase 0 baseline evidence and flip Phase 0 tasks Co-Authored-By: Claude Sonnet 5.5 --- .../p0-t10-poshqc-format.2026-10-02T03-08.md | 38 ++++++++++++ .../p0-t11-poshqc-analyze.2026-10-02T03-08.md | 19 ++++++ .../p0-t12-poshqc-test.2026-10-02T03-08.md | 57 +++++++++++++++++ ...p0-t13-budget-baseline.2026-10-02T03-08.md | 37 +++++++++++ .../p0-t2-base-anchor.2026-10-02T03-08.md | 39 ++++++++++++ .../p0-t3-ac-precondition.2026-10-02T03-08.md | 16 +++++ .../p0-t4-fizzler-census.2026-10-02T03-08.md | 31 ++++++++++ ...5-shared-string-census.2026-10-02T03-08.md | 46 ++++++++++++++ .../p0-t6-unsafe-census.2026-10-02T03-08.md | 31 ++++++++++ ...0-t7-encoding-baseline.2026-10-02T03-08.md | 61 +++++++++++++++++++ ...8-known-debt-remeasure.2026-10-02T03-08.md | 48 +++++++++++++++ ...-t9-linecount-baseline.2026-10-02T03-08.md | 26 ++++++++ ...ase0-instructions-read.2026-10-02T03-08.md | 33 ++++++++++ .../plan.2026-10-02T00-16.md | 26 ++++---- 14 files changed, 495 insertions(+), 13 deletions(-) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t10-poshqc-format.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t11-poshqc-analyze.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t12-poshqc-test.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t13-budget-baseline.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t2-base-anchor.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t3-ac-precondition.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t4-fizzler-census.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t5-shared-string-census.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t6-unsafe-census.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t7-encoding-baseline.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t8-known-debt-remeasure.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t9-linecount-baseline.2026-10-02T03-08.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t10-poshqc-format.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t10-poshqc-format.2026-10-02T03-08.md new file mode 100644 index 000000000..b7a1e97e0 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t10-poshqc-format.2026-10-02T03-08.md @@ -0,0 +1,38 @@ +# P0-T10 Baseline PowerShell format step + +Timestamp: 2026-10-02T03-08 +Command: git -C hash-object <14 paths> (CMD-HASHSET before); MCP mcp__drm-copilot__run_poshqc_format with workspace_root `` and scan_folders `["scripts/dependencies","tests/scripts/dependencies"]`; git -C hash-object <14 paths> (CMD-HASHSET after); git -C status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies +EXIT_CODE: 0 + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_format","workspace_root":"","summary":"Ran bundled PoshQC format against '' with 2 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): `ok` is true, so 0. + +HASH set before and after (identical; the 14 hashes were obtained in one `git hash-object` invocation each time, sorted by path): + +```text +HASH scripts/dependencies/AnalyzerItemRepair.psm1 97e07385e44d57462975875d03dc639a0cf37448 +HASH scripts/dependencies/ConsistencyVerifier.psm1 a3d71fac2681531816fc4d9f6062e4a47fbf7a84 +HASH scripts/dependencies/PackageCompatibility.psm1 0a7a10463911d2e4d9bea73522a6b1bb4aa97c56 +HASH scripts/dependencies/PackageGraph.psm1 ca1579f7eb17066fe86578a6b5f04c92e3682a18 +HASH scripts/dependencies/ProjectConsistency.psm1 334c1b5bf74d0d5f688aae7532f7498246b0a6d4 +HASH scripts/dependencies/Repair-PackageManifestConsistency.ps1 6d13903d57eab1d0f751ff0452c989103500bf35 +HASH tests/scripts/dependencies/AnalyzerItemRepair.Tests.ps1 8c9be4625935462e5c1d59200dcaaf3978b0277b +HASH tests/scripts/dependencies/ConsistencyVerifier.Tests.ps1 26e5d3e8be66c729b2d67d423e93729d3ed0c06f +HASH tests/scripts/dependencies/DependabotConfig.Tests.ps1 b740c118927caf1ef1aed74250602b2ea43f09d7 +HASH tests/scripts/dependencies/PackageCompatibility.Tests.ps1 d5409dff1556ef0770f98b51db1c2f1d6abd9428 +HASH tests/scripts/dependencies/PackageGraph.Tests.ps1 2977c2c7527b120ab807e171b102366b924cbf89 +HASH tests/scripts/dependencies/ProjectConsistency.Tests.ps1 57dc48c5439f01b21bc0948fe9369451108ec0d9 +HASH tests/scripts/dependencies/Repair-PackageManifestConsistency.Tests.ps1 7c8d6b697a99ad9c48d79448806a6cf629dafb7f +HASH tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 4fbea957f91aa94fb90c03e829a2783208879a0b +``` + +Porcelain over the two folders after the format call: empty (no output). + +Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical (14 of 14 hashes equal); porcelain over the two folders is empty. No FORMAT-DRIFT-REVERTED entries. + +Output Summary: PoshQC format ok true, 2 scan folders; hash sets identical before and after; porcelain empty; the formatter rewrote no file. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t11-poshqc-analyze.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t11-poshqc-analyze.2026-10-02T03-08.md new file mode 100644 index 000000000..f8356b6c9 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t11-poshqc-analyze.2026-10-02T03-08.md @@ -0,0 +1,19 @@ +# P0-T11 Baseline PowerShell analyze step + +Timestamp: 2026-10-02T03-08 +Command: MCP mcp__drm-copilot__run_poshqc_analyze with workspace_root `` and scan_folders `["scripts/dependencies","tests/scripts/dependencies"]` +EXIT_CODE: 0 + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_analyze","workspace_root":"","summary":"Ran bundled PoshQC analyze against '' with 2 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): `ok` is true, so 0. The payload carries no count, file or rule. + +GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count + +Acceptance: `ok` true with the 2-folder summary literal. No STOP: ANALYZE-BASELINE-RED. + +Output Summary: PoshQC analyze: pass (0 findings); tool reports no count. ok true over scripts/dependencies and tests/scripts/dependencies. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t12-poshqc-test.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t12-poshqc-test.2026-10-02T03-08.md new file mode 100644 index 000000000..f088e9d78 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t12-poshqc-test.2026-10-02T03-08.md @@ -0,0 +1,57 @@ +# P0-T12 Baseline PowerShell test step + +Timestamp: 2026-10-02T03-08 +Command: pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue' (CMD-JUNIT-DELETE); Glob `artifacts/pester/pester-junit.xml`; MCP mcp__drm-copilot__run_poshqc_test with workspace_root `` and scan_folders `["tests/scripts/dependencies"]`; CMD-JUNIT-READ Greps (`/artifacts/pester/pester-junit.xml` +EXIT_CODE: 0 +ExpectedExitCode: 0 + +CMD-JUNIT-DELETE observations: the pwsh invocation returned exit code 1 as observed (the Bash tool reported `Exit code 1` with no message). With `-ErrorAction SilentlyContinue`, `Remove-Item` on a path that does not exist records a non-terminating error and the `-Command` host then exits 1, so the observation is consistent with no pre-existing document; it is recorded as observed and not relied on. The Glob for `artifacts/pester/pester-junit.xml` returned no files after the delete. + +GLOB-BLIND: after the test run the same Glob again returned `No files found` although the Grep tool read the document by absolute path (lines below). The plan's round 3 observation that Glob returns this gitignored file with a worktree root as its path did not reproduce at this worktree. The existence precondition of CMD-JUNIT-READ was therefore established by the successful Greps, and the document's freshness rests on the delete step plus the document content: a full 8-suite run with the worktree path in every suite name. + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_test","workspace_root":"","summary":"Ran bundled PoshQC test against '' with 1 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): payload `ok` true and JUNIT root `failures=0`, so 0. + +CMD-JUNIT-READ: + +```text +JUNIT-ROOT tests=137 failures=0 errors=0 disabled=0 +JUNIT-SUITE AnalyzerItemRepair.Tests.ps1 tests=13 failures=0 skipped=0 +JUNIT-SUITE ConsistencyVerifier.Tests.ps1 tests=14 failures=0 skipped=0 +JUNIT-SUITE DependabotConfig.Tests.ps1 tests=17 failures=0 skipped=0 +JUNIT-SUITE PackageCompatibility.Tests.ps1 tests=8 failures=0 skipped=0 +JUNIT-SUITE PackageGraph.Tests.ps1 tests=32 failures=0 skipped=0 +JUNIT-SUITE ProjectConsistency.Tests.ps1 tests=18 failures=0 skipped=0 +JUNIT-SUITE Repair-PackageManifestConsistency.Tests.ps1 tests=31 failures=0 skipped=0 +JUNIT-SUITE RepositoryTreeConsistency.Tests.ps1 tests=4 failures=0 skipped=0 +JUNIT-NOTPASSED: none +``` + +The ``; read-only; no state file opened for writing) +EXIT_CODE: 0 + +Quoted regions of the hook (line numbers from the Read output): + +Lines 10-11 (caps): + +```text + - 3 production PowerShell files per batch + - 3 test PowerShell files per batch +``` + +Line 14 (state file location; the sentence begins on line 13 and the path is on line 14): + +```text + persisted under .claude/state/powershell-batch-budget..json. Only +``` + +Lines 42-45 (deny behaviour): + +```text + When the cap would be exceeded by a new file, the script emits a PreToolUse JSON + response with hookSpecificOutput.permissionDecision = 'deny' and exits 0. The session + must explicitly reset the counter by deleting the state file before starting a new + batch. Files already counted are always allowed through. +``` + +State-file observation: Glob `.claude/state/powershell-batch-budget.*.json` returned no files in the execution worktree, and Glob `*` under `.claude/state` also returned no files. The Glob tool has been blind to gitignored paths at this worktree (see p0-t12 GLOB-BLIND), so the result is recorded as "no state file observed in the execution worktree" and not as proof of absence. The coordinator reported the session budget as production 1 of 3 and tests 2 of 3 used; this item needs 1 production slot (scripts/dependencies/BindingRedirectVerification.psm1) and 1 test slot (tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1). + +D9 stop rule restated: if any Write or Edit to a PowerShell path is denied, the executor records `BUDGET-DENIED:` with the hook message and stops; it does not delete or edit the state file under `.claude/state/`, does not set `CLAUDE_POWERSHELL_BUDGET_PROD` or `CLAUDE_POWERSHELL_BUDGET_TEST`, and does not split the batch on its own authority. + +BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable + +Output Summary: Hook caps are 3 production and 3 test files per session batch; deny is emitted on the write that would exceed a cap. No state file observed in the execution worktree. The D9 stop rule is restated. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t2-base-anchor.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t2-base-anchor.2026-10-02T03-08.md new file mode 100644 index 000000000..876d37494 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t2-base-anchor.2026-10-02T03-08.md @@ -0,0 +1,39 @@ +# P0-T2 Base git anchor + +Timestamp: 2026-10-02T03-08 +Command: git -C fetch origin main; git -C merge-base HEAD origin/main; git -C rev-parse HEAD; git -C cat-file -t ; git -C status --porcelain --untracked-files=all; git -C diff --name-only -- . +EXIT_CODE: 0 + +FETCH-EXIT: 0 +BASE_SHA: 860d67bf4fddecb929e0d6c166065fd1ee752feb +P0-START: 66bc53ee1f37118ebc0ffeb79d23292606cabb35 +CAT-FILE-TYPE: commit +ORIGIN-MAIN-TIP: 860d67bf4fddecb929e0d6c166065fd1ee752feb (equals BASE_SHA, as the orchestrator stated after the origin/main merge into the branch) + +BASE-PORCELAIN (verbatim; taken after P0-T1 had written its artifact and flipped its plan checkbox, so both entries are P0-T1 outputs): + +```text + M docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md +``` + +INHERITED (verbatim): + +```text +.claude/agent-memory/atomic-planner/MEMORY.md +.claude/agent-memory/atomic-planner/project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md +.claude/agent-memory/atomic-planner/project_953_r1_pwsh_refused_and_count_recheck_seams.md +.claude/agent-memory/atomic-planner/project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md +.claude/agent-memory/atomic-planner/project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/preflight-clearance.2026-10-02T03-45.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md +docs/features/potential/promoted/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md +``` + +Git emitted one advisory on the diff command: the working copy of the plan file will have LF replaced by CRLF the next time Git touches it (autocrlf advisory; not an error). + +Acceptance: BASE_SHA is 40 hexadecimal characters; `git cat-file -t` prints `commit`; no path in BASE-PORCELAIN or INHERITED ends in `.cs`, `.csproj`, `packages.config` or `app.config`, and none lies under `scripts/dependencies/` or `tests/scripts/dependencies/`. The feature-folder paths and `.claude/agent-memory/` paths are recorded, not asserted empty. + +Output Summary: BASE_SHA 860d67bf4fddecb929e0d6c166065fd1ee752feb equals the origin/main tip. HEAD 66bc53ee1f37118ebc0ffeb79d23292606cabb35. Write Set is clean at start; INHERITED lists 10 paths (agent-memory and feature-folder documents only). diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t3-ac-precondition.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t3-ac-precondition.2026-10-02T03-08.md new file mode 100644 index 000000000..279c7036e --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t3-ac-precondition.2026-10-02T03-08.md @@ -0,0 +1,16 @@ +# P0-T3 Acceptance-criteria source and mode precondition + +Timestamp: 2026-10-02T03-08 +Command: Read of issue.md; Grep count `^## Acceptance Criteria$`; Grep count `^- \[ \] AC[1-6]:`; Grep count `^- \[x\] AC[1-6]:`; Grep `is not measured locally`; Glob `{spec.md,user-story.md}` in the feature folder (all paths rooted at ``) +EXIT_CODE: 0 + +Observations: + +- issue.md line 9 reads `- Work Mode: minor-audit` (Read output, line 9). +- Count of `^## Acceptance Criteria$` = 1 (expected 1). +- Count of `^- \[ \] AC[1-6]:` = 6 (expected 6). +- Count of `^- \[x\] AC[1-6]:` = 0 (expected 0). +- AC6 literal present: line 89 contains `is not measured locally`. +- Glob for spec.md and user-story.md in the feature folder: no files found. + +Output Summary: All five conditions hold; no STOP: AC-SOURCE. Mode minor-audit, one AC section, 6 unchecked AC items, 0 checked, AC6 carries the CI-coverage literal, no spec.md or user-story.md. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t4-fizzler-census.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t4-fizzler-census.2026-10-02T03-08.md new file mode 100644 index 000000000..124a4a375 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t4-fizzler-census.2026-10-02T03-08.md @@ -0,0 +1,31 @@ +# P0-T4 Baseline Fizzler census over app.config files + +Timestamp: 2026-10-02T03-08 +Command: Grep pattern `name="Fizzler"` with -A 1 and -n, content mode, path ``, glob `**/app.config` +EXIT_CODE: 0 + +GLOB-SUBSTITUTION: the plan's glob `*/app.config` returned no match under the Grep tool at this worktree depth (the worktree sits under a gitignored `.claude/worktrees/` parent; a Grep of the single file QuickFiler/app.config returned the expected lines). The glob `**/app.config` was used. Its result set is exactly the 13 root-level `/app.config` files, so it is the same set the plan's glob names; no nested app.config file appears. + +Observed (path:line of the assemblyIdentity line, then the following redirect line number and newVersion): + +```text +TaskTree/app.config 46 | 47 newVersion="1.3.0.0" +TaskVisualization.Test/app.config 46 | 47 newVersion="1.3.0.0" +TaskMaster/app.config 50 | 51 newVersion="1.3.0.0" +Tags/app.config 46 | 47 newVersion="1.3.0.0" +TaskVisualization/app.config 46 | 47 newVersion="1.3.0.0" +UtilitiesCS.Test/app.config 46 | 47 newVersion="1.3.0.0" +QuickFiler.Test/app.config 46 | 47 newVersion="1.3.0.0" +SVGControl.Test/app.config 18 | 19 newVersion="1.3.0.0" +SVGControl/app.config 14 | 15 newVersion="1.3.1.0" +QuickFiler/app.config 50 | 51 newVersion="1.3.0.0" +ToDoModel/app.config 51 | 52 newVersion="1.3.0.0" +UtilitiesCS/app.config 51 | 52 newVersion="1.3.1.0" +ToDoModel.Test/app.config 46 | 47 newVersion="1.3.0.0" +``` + +Every stale redirect line reads ``; the two correct lines read ``. + +Acceptance: 13 blocks. The 11 stale files at the stated lines are exactly QuickFiler 51, QuickFiler.Test 47, SVGControl.Test 19, Tags 47, TaskMaster 51, TaskTree 47, TaskVisualization 47, TaskVisualization.Test 47, ToDoModel 52, ToDoModel.Test 47, UtilitiesCS.Test 47. SVGControl 15 and UtilitiesCS 52 read 1.3.1.0. No TREE-DRIFT. + +Output Summary: 13 Fizzler blocks; 11 stale at 1.3.0.0 at the plan's line numbers; 2 at 1.3.1.0 (SVGControl 15, UtilitiesCS 52). Matches the plan; no drift. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t5-shared-string-census.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t5-shared-string-census.2026-10-02T03-08.md new file mode 100644 index 000000000..0102a43ce --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t5-shared-string-census.2026-10-02T03-08.md @@ -0,0 +1,46 @@ +# P0-T5 Baseline shared-string census over app.config files + +Timestamp: 2026-10-02T03-08 +Command: Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` count mode, glob `**/app.config`; Grep pattern `name="System.ClientModel"` with -A 1 and -n, content mode, glob `**/app.config` (path ``; glob substitution as recorded in p0-t4) +EXIT_CODE: 0 + +Shared-string counts (17 occurrences in 11 files): + +```text +TaskTree/app.config 2 +Tags/app.config 2 +TaskMaster/app.config 2 +QuickFiler/app.config 2 +ToDoModel/app.config 2 +TaskVisualization/app.config 2 +UtilitiesCS.Test/app.config 1 +SVGControl.Test/app.config 1 +QuickFiler.Test/app.config 1 +ToDoModel.Test/app.config 1 +TaskVisualization.Test/app.config 1 +``` + +System.ClientModel blocks: 16 (SVGControl/app.config carries none). Per-file values (line is the redirect line): + +```text +CLIENTMODEL TaskTree/app.config=1.3.0.0 (line 103) +CLIENTMODEL TaskMaster/app.config=1.3.0.0 (line 115) +CLIENTMODEL Tags/app.config=1.3.0.0 (line 103) +CLIENTMODEL QuickFiler/app.config=1.3.0.0 (line 103) +CLIENTMODEL TaskVisualization/app.config=1.3.0.0 (line 103) +CLIENTMODEL ToDoModel/app.config=1.3.0.0 (line 108) +CLIENTMODEL VBFunctions.Test/app.config=1.16.0.0 (line 99) +CLIENTMODEL SVGControl.Test/app.config=1.16.0.0 (line 47) +CLIENTMODEL TaskMaster.Test/app.config=1.16.0.0 (line 99) +CLIENTMODEL UtilitiesCS.Test/app.config=1.16.0.0 (line 123) +CLIENTMODEL Tags.Test/app.config=1.16.0.0 (line 223) +CLIENTMODEL QuickFiler.Test/app.config=1.16.0.0 (line 103) +CLIENTMODEL UtilitiesCS/app.config=1.16.0.0 (line 108) +CLIENTMODEL TaskVisualization.Test/app.config=1.16.0.0 (line 103) +CLIENTMODEL ToDoModel.Test/app.config=1.16.0.0 (line 103) +CLIENTMODEL TaskTree.Test/app.config=1.16.0.0 (line 223) +``` + +Acceptance: both expectations hold. 17 occurrences across 11 files with 2 each in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel and 1 each in QuickFiler.Test, SVGControl.Test, TaskVisualization.Test, ToDoModel.Test, UtilitiesCS.Test. 16 System.ClientModel blocks, at 1.3.0.0 for exactly Tags 103, TaskVisualization 103, TaskTree 103, QuickFiler 103, TaskMaster 115, ToDoModel 108 and at 1.16.0.0 for the other 10. No TREE-DRIFT. + +Output Summary: 17 shared-string occurrences in 11 files; 16 System.ClientModel blocks (6 at 1.3.0.0, 10 at 1.16.0.0) at the plan's line numbers. Matches the plan. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t6-unsafe-census.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t6-unsafe-census.2026-10-02T03-08.md new file mode 100644 index 000000000..ffe54aa04 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t6-unsafe-census.2026-10-02T03-08.md @@ -0,0 +1,31 @@ +# P0-T6 Baseline Unsafe census over app.config files (AC2 evidence) + +Timestamp: 2026-10-02T03-08 +Command: Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with -A 1 and -n, content mode, path ``, glob `**/app.config` (glob substitution as recorded in p0-t4) +EXIT_CODE: 0 + +17 blocks; the line following every assemblyIdentity line reads ``. Files (assemblyIdentity line number in parentheses): + +```text +QuickFiler/app.config (6) +QuickFiler.Test/app.config (18) +SVGControl/app.config (18) +SVGControl.Test/app.config (22) +Tags/app.config (18) +Tags.Test/app.config (10) +TaskMaster/app.config (22) +TaskMaster.Test/app.config (10) +TaskTree/app.config (18) +TaskTree.Test/app.config (10) +TaskVisualization/app.config (18) +TaskVisualization.Test/app.config (18) +ToDoModel/app.config (11) +ToDoModel.Test/app.config (18) +UtilitiesCS/app.config (11) +UtilitiesCS.Test/app.config (14) +VBFunctions.Test/app.config (10) +``` + +Acceptance: 17 blocks, every following line reads `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; the 17 files are listed above. + +Output Summary: 17 Unsafe redirect blocks, all at 6.0.3.0 (AC2 baseline holds; verified, not edited). diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t7-encoding-baseline.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t7-encoding-baseline.2026-10-02T03-08.md new file mode 100644 index 000000000..098f5e239 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t7-encoding-baseline.2026-10-02T03-08.md @@ -0,0 +1,61 @@ +# P0-T7 Baseline encoding observation for the 11 Write Set configs + +Timestamp: 2026-10-02T03-08 +Command: git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath / -AsByteStream -TotalCount 3) -join [char]44' (once per path, 11 invocations); Grep pattern `^` count mode and Grep pattern `\r$` count mode, glob `**/app.config`, path `` (glob substitution as recorded in p0-t4; the 11 Write Set files are read from the 17-file result) +EXIT_CODE: 0 + +CMD-EOL (first field is the index terminator and is recorded, not gated; the second field is the gated working-tree terminator): + +```text +i/lf w/crlf QuickFiler/app.config +i/lf w/crlf QuickFiler.Test/app.config +i/lf w/crlf SVGControl.Test/app.config +i/lf w/crlf Tags/app.config +i/lf w/crlf TaskMaster/app.config +i/lf w/crlf TaskTree/app.config +i/lf w/crlf TaskVisualization/app.config +i/lf w/crlf TaskVisualization.Test/app.config +i/lf w/crlf ToDoModel/app.config +i/lf w/crlf ToDoModel.Test/app.config +i/lf w/crlf UtilitiesCS.Test/app.config +``` + +Every line carries `attr/text=auto`. The 11 paths were passed in one `git ls-files --eol` invocation rather than 11; the output is one line per path, identical in form to the per-path call. + +CMD-BOM: + +```text +BOM-BYTES QuickFiler/app.config=239,187,191 +BOM-BYTES QuickFiler.Test/app.config=239,187,191 +BOM-BYTES SVGControl.Test/app.config=239,187,191 +BOM-BYTES Tags/app.config=239,187,191 +BOM-BYTES TaskMaster/app.config=239,187,191 +BOM-BYTES TaskTree/app.config=239,187,191 +BOM-BYTES TaskVisualization/app.config=239,187,191 +BOM-BYTES TaskVisualization.Test/app.config=239,187,191 +BOM-BYTES ToDoModel/app.config=239,187,191 +BOM-BYTES ToDoModel.Test/app.config=239,187,191 +BOM-BYTES UtilitiesCS.Test/app.config=239,187,191 +``` + +CMD-LINECOUNT (`^` count) paired with `\r$` count: + +```text +LINECOUNT QuickFiler/app.config lines=243 cr=243 equal +LINECOUNT QuickFiler.Test/app.config lines=367 cr=367 equal +LINECOUNT SVGControl.Test/app.config lines=227 cr=227 equal +LINECOUNT Tags/app.config lines=239 cr=239 equal +LINECOUNT TaskMaster/app.config lines=430 cr=430 equal +LINECOUNT TaskTree/app.config lines=239 cr=239 equal +LINECOUNT TaskVisualization/app.config lines=239 cr=239 equal +LINECOUNT TaskVisualization.Test/app.config lines=363 cr=363 equal +LINECOUNT ToDoModel/app.config lines=323 cr=323 equal +LINECOUNT ToDoModel.Test/app.config lines=363 cr=363 equal +LINECOUNT UtilitiesCS.Test/app.config lines=383 cr=383 equal +``` + +All eleven counts equal the plan's expected values. + +Acceptance: 11 `w/crlf` lines, 11 BOM-BYTES lines (all `239,187,191`, so the Phase 1 preservation reference is `239,187,191` for every path), 11 equal count pairs. + +Output Summary: All 11 Write Set configs are UTF-8 BOM (239,187,191) with CRLF in the working tree (w/crlf), and every line carries a CR (line count equals CR count). Matches the plan's expected line counts. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t8-known-debt-remeasure.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t8-known-debt-remeasure.2026-10-02T03-08.md new file mode 100644 index 000000000..109184373 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t8-known-debt-remeasure.2026-10-02T03-08.md @@ -0,0 +1,48 @@ +# P0-T8 Baseline re-measurement of the known-debt table + +Timestamp: 2026-10-02T03-08 +Command: Grep count mode glob `**/app.config` patterns `assemblyIdentity name=` and `"` for each of the 3 unverifiable names (path ``; the plan's `*/app.config` and `*/*.csproj` globs return no match under the Grep and Glob tools at this worktree depth, as recorded in p0-t4, so `**/...` globs were used; each result set was confirmed to contain only root-level files) +EXIT_CODE: 0 + +SUBSTITUTION: the 15 per-row `Include=` Greps were issued as one alternation Grep whose `-o` output carries the file, line and matched text for every name. The alternation requires `, Version=` immediately after each name, so `Microsoft.Identity.Client` does not match the `.Extensions.Msal` sibling. + +(a) Totals: + +- `assemblyIdentity name=` count: 1176 across 17 files. +- `/.csproj` at root level. + +(b) Per-row results (config files carrying the stale pair, every per-file count 1; csproj Reference version printed): + +```text +ROW 1 Azure.Core | 1.62.0.0 | files=6 | per-file count 1 | csproj Version=1.63.0.0 only +ROW 2 Microsoft.Bcl.Memory | 10.0.0.7 | files=10 | per-file count 1 | csproj Version=10.0.0.12 only +ROW 3 Microsoft.Bcl.Numerics | 10.0.0.5 | files=12 | per-file count 1 | csproj Version=10.0.0.12 only +ROW 4 Microsoft.Extensions.Diagnostics.Abstractions | 10.0.0.5 | files=6 | per-file count 1 | csproj Version=10.0.0.12 only +ROW 5 Microsoft.Identity.Client | 4.89.0.0 | files=6 | per-file count 1 | csproj Version=4.90.1.0 only +ROW 6 Microsoft.Identity.Client.Extensions.Msal | 4.89.0.0 | files=6 | per-file count 1 | csproj Version=4.90.1.0 only +ROW 7 Microsoft.IdentityModel.Abstractions | 8.22.0.0 | files=6 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 8 Microsoft.IdentityModel.JsonWebTokens | 8.22.0.0 | files=7 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 9 Microsoft.IdentityModel.Logging | 8.22.0.0 | files=7 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 10 Microsoft.IdentityModel.Protocols | 8.22.0.0 | files=13 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 11 Microsoft.IdentityModel.Protocols.OpenIdConnect | 8.22.0.0 | files=13 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 12 Microsoft.IdentityModel.Tokens | 8.22.0.0 | files=13 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 13 Microsoft.IdentityModel.Validators | 8.22.0.0 | files=13 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 14 System.IdentityModel.Tokens.Jwt | 8.22.0.0 | files=13 | per-file count 1 | csproj Version=8.23.0.0 only +ROW 15 System.ClientModel | 1.3.0.0 | files=6 | per-file count 1 | csproj Version=1.16.0.0 only +``` + +Sum of the config counts: 6+10+12+6+6+6+6+7+7+13+13+13+13+13+6 = 137 entries, equal to section 7. + +Every csproj `-o` match for the 15 names ends in the row's Reference version. Rows 1, 3 through 15 print their versions from the csproj files listed by the Grep (for example UtilitiesCS/UtilitiesCS.csproj lines 51 through 351 carry all 15 names); no other version was printed for any name. + +(c) Unverifiable names: + +- Grep `Include="(System.Linq.AsyncEnumerable|Microsoft.IdentityModel.Clients.ActiveDirectory|netstandard)` over `**/*.csproj`: no match. +- `name="System.Linq.AsyncEnumerable"` over `**/app.config`: 15 files, 15 total. +- `name="Microsoft.IdentityModel.Clients.ActiveDirectory"` over `**/app.config`: 13 files, 13 total. +- `name="netstandard"` over `**/app.config`: 1 file (TaskMaster/app.config), 1 total. + +Acceptance: every figure equals section 7 (1176, 1176, 18; 15 rows with config counts 6, 10, 12, 6, 6, 6, 6, 7, 7, 13, 13, 13, 13, 13, 6 and the stated Reference versions; unverifiable counts 15, 13, 1 with no csproj Reference). No `KNOWN-DEBT-DRIFT`. The orchestrator's note that origin/main was merged after the plan's measurement does not change any figure: no numeric difference from the plan's section 4 or section 7 tables was observed in P0-T4 through P0-T8. + +Output Summary: Known-debt table re-measured on the merged tree; all 15 rows (137 entries), 3 unverifiable names (29 entries), 1176 redirect entries, 17 configs and 18 csproj equal the plan. No drift. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t9-linecount-baseline.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t9-linecount-baseline.2026-10-02T03-08.md new file mode 100644 index 000000000..6f6fd3b3c --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t9-linecount-baseline.2026-10-02T03-08.md @@ -0,0 +1,26 @@ +# P0-T9 Baseline line counts + +Timestamp: 2026-10-02T03-08 +Command: Grep pattern `^` count mode on each file (CMD-LINECOUNT); Glob `**/BindingRedirectVerification*` under `scripts/dependencies` and under `tests/scripts/dependencies`; Glob `**/*` under each of the two folders to confirm the Glob tool is not blind there (paths rooted at ``) +EXIT_CODE: 0 + +Line counts: + +```text +LINECOUNT scripts/dependencies/PackageGraph.psm1 = 465 (expected 465) +LINECOUNT scripts/dependencies/ProjectConsistency.psm1 = 381 (expected 381) +LINECOUNT scripts/dependencies/ConsistencyVerifier.psm1 = 499 (expected 499) +LINECOUNT tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 = 152 (expected 152) +``` + +No LINECOUNT-DRIFT. + +New-file absence: + +- Glob `**/BindingRedirectVerification*` under `scripts/dependencies`: no files found. +- Glob `**/BindingRedirectVerification*` under `tests/scripts/dependencies`: no files found. +- Control: Glob `**/*` under `scripts/dependencies` lists 6 files (AnalyzerItemRepair.psm1, ConsistencyVerifier.psm1, PackageCompatibility.psm1, PackageGraph.psm1, ProjectConsistency.psm1, Repair-PackageManifestConsistency.ps1); under `tests/scripts/dependencies` it lists 8 files (AnalyzerItemRepair, ConsistencyVerifier, DependabotConfig, PackageCompatibility, PackageGraph, ProjectConsistency, Repair-PackageManifestConsistency, RepositoryTreeConsistency, each with the `.Tests.ps1` suffix). The CMD-HASHSET population is therefore 14 files. + +Acceptance: the four counts equal the stated figures and both new-file Globs return nothing; no PREEXISTING-FILE stop. + +Output Summary: PackageGraph 465, ProjectConsistency 381, ConsistencyVerifier 499, RepositoryTreeConsistency.Tests 152; neither new file exists. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md new file mode 100644 index 000000000..247d512d3 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md @@ -0,0 +1,33 @@ +# Phase 0 Instructions Read (P0-T1) + +Timestamp: 2026-10-02T03-08 + +Policy Order: + +1. CLAUDE.md +2. .claude/rules/general-code-change.md +3. .claude/rules/general-unit-test.md +4. .claude/rules/powershell.md +5. .claude/rules/plan-acceptance-gates.md +6. .claude/rules/tonality.md +7. .claude/skills/atomic-plan-contract/SKILL.md +8. .claude/skills/evidence-and-timestamp-conventions/SKILL.md +9. .claude/skills/acceptance-criteria-tracking/SKILL.md +10. docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +11. docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md + +Files read (all paths relative to ``): + +- CLAUDE.md +- .claude/rules/general-code-change.md +- .claude/rules/general-unit-test.md +- .claude/rules/powershell.md +- .claude/rules/plan-acceptance-gates.md +- .claude/rules/tonality.md +- .claude/skills/atomic-plan-contract/SKILL.md +- .claude/skills/evidence-and-timestamp-conventions/SKILL.md +- .claude/skills/acceptance-criteria-tracking/SKILL.md +- docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +- docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md + +Output Summary: 11 of 11 files read. The three skill files and CLAUDE.md, general-code-change, general-unit-test, quality-tiers and tonality rules were supplied in the session context as loaded copies of the execution worktree files; powershell.md, the plan and issue.md were read with the Read tool. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index ed3440804..3f9975a02 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -338,19 +338,19 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on ### Phase 0 — Baseline capture and policy reads -- [ ] [P0-T1] Read the policy and skill files in order and record the read: CLAUDE.md, `.claude/rules/general-code-change.md`, `.claude/rules/general-unit-test.md`, `.claude/rules/powershell.md`, `.claude/rules/plan-acceptance-gates.md`, `.claude/rules/tonality.md`, `.claude/skills/atomic-plan-contract/SKILL.md`, `.claude/skills/evidence-and-timestamp-conventions/SKILL.md`, `.claude/skills/acceptance-criteria-tracking/SKILL.md`, then this plan and issue.md. Artifact `evidence/baseline/phase0-instructions-read..md` with `Timestamp:`, `Policy Order:` (the numbered order above) and the explicit list of files read. Acceptance: the artifact exists and lists all eleven files. -- [ ] [P0-T2] Record the baseline git anchor for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: run `git fetch origin main` (record `FETCH-EXIT:`; a non-zero value is recorded and the run continues against the local `origin/main` ref with the line `FETCH-STALE: local origin/main used`), then `git merge-base HEAD origin/main` → `BASE_SHA:`, `git rev-parse HEAD` → `P0-START:`, `git cat-file -t `, `git status --porcelain --untracked-files=all` → `BASE-PORCELAIN:` (verbatim), `git diff --name-only -- .` → `INHERITED:` (verbatim; this is the set every later footprint evaluation subtracts, C6). Acceptance: BASE_SHA is 40 hexadecimal characters and `git cat-file -t` prints `commit`; no path in BASE-PORCELAIN or INHERITED ends in `.cs`, `.csproj`, `packages.config` or `app.config`, and none lies under `scripts/dependencies/` or `tests/scripts/dependencies/` (the Write Set is clean at start); the feature folder paths and `.claude/agent-memory/` paths are expected and recorded, never asserted empty. Artifact `evidence/baseline/p0-t2-base-anchor..md`. -- [ ] [P0-T3] Verify the acceptance-criteria source and mode as a baseline precondition by reading `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md`: line 9 reads `- Work Mode: minor-audit`; exactly one `## Acceptance Criteria` heading (Grep count of `^## Acceptance Criteria$` is 1); Grep count of `^- \[ \] AC[1-6]:` is 6 and of `^- \[x\] AC[1-6]:` is 0; the AC6 line contains the literal `is not measured locally`; Glob for `spec.md` and `user-story.md` in the feature folder returns nothing. Any failed condition is STOP: AC-SOURCE. Artifact `evidence/baseline/p0-t3-ac-precondition..md`. -- [ ] [P0-T4] Baseline Fizzler census over `*/app.config`: Grep pattern `name="Fizzler"` with `-A 1`, glob `*/app.config`, content mode. Acceptance: 13 blocks; the following line reads `newVersion="1.3.0.0"` for exactly these 11 files at these lines — QuickFiler/app.config 51, QuickFiler.Test/app.config 47, SVGControl.Test/app.config 19, Tags/app.config 47, TaskMaster/app.config 51, TaskTree/app.config 47, TaskVisualization/app.config 47, TaskVisualization.Test/app.config 47, ToDoModel/app.config 52, ToDoModel.Test/app.config 47, UtilitiesCS.Test/app.config 47 — and reads `newVersion="1.3.1.0"` for SVGControl/app.config 15 and UtilitiesCS/app.config 52. A different member set or line is STOP: TREE-DRIFT (the Write Set line citations would be wrong). Artifact `evidence/baseline/p0-t4-fizzler-census..md`. -- [ ] [P0-T5] Baseline shared-string census over `*/app.config`: Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → expected 17 occurrences across 11 files, 2 each in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel and 1 each in QuickFiler.Test, SVGControl.Test, TaskVisualization.Test, ToDoModel.Test, UtilitiesCS.Test; Grep pattern `name="System.ClientModel"` with `-A 1` → 16 blocks (SVGControl/app.config carries none), the following line at 1.3.0.0 for exactly Tags 103, TaskVisualization 103, TaskTree 103, QuickFiler 103, TaskMaster 115, ToDoModel 108 and at 1.16.0.0 for the other 10. Acceptance: both expectations hold (otherwise STOP: TREE-DRIFT). The artifact records the per-file System.ClientModel values as `CLIENTMODEL =` lines for P1 comparison. Artifact `evidence/baseline/p0-t5-shared-string-census..md`. -- [ ] [P0-T6] Baseline Unsafe census over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1`, glob `*/app.config`. Acceptance: 17 blocks and every following line reads `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; the artifact lists the 17 files. Artifact `evidence/baseline/p0-t6-unsafe-census..md`. -- [ ] [P0-T7] Baseline encoding observation for the 11 Write Set configs (`QuickFiler/app.config` and the ten others of section 5): for each path run CMD-EOL (second whitespace-separated field must read `w/crlf`), CMD-BOM (record `BOM-BYTES =`; expected `239,187,191`; a different value is recorded, not a stop, and becomes the preservation reference for Phase 1), and CMD-LINECOUNT paired with Grep pattern `\r$` count mode on the same file (the two counts must be equal; expected values QuickFiler 243, QuickFiler.Test 367, SVGControl.Test 227, Tags 239, TaskMaster 430, TaskTree 239, TaskVisualization 239, TaskVisualization.Test 363, ToDoModel 323, ToDoModel.Test 363, UtilitiesCS.Test 383). Acceptance: 11 `w/crlf` lines, 11 BOM-BYTES lines, 11 equal count pairs. Artifact `evidence/baseline/p0-t7-encoding-baseline..md`. -- [ ] [P0-T8] Baseline re-measurement of the known-debt table over `*/app.config` and `*/*.csproj`: (a) Grep count mode, glob `*/app.config`, pattern `assemblyIdentity name=` → 1176 total across 17 files; pattern `` and no other version is printed; (c) for each of the 3 unverifiable names, Grep glob `*/*.csproj`, pattern `Include="` → no match, and Grep glob `*/app.config`, pattern `name=""` count mode → 15, 13, 1 respectively. Acceptance: every figure equals section 7; any difference is recorded as `KNOWN-DEBT-DRIFT: ` and the run stops (D7). Completeness beyond the 18 names is proven by test 14 at P1-T15, not here. Artifact `evidence/baseline/p0-t8-known-debt-remeasure..md`. -- [ ] [P0-T9] Baseline line counts (CMD-LINECOUNT) for `scripts/dependencies/PackageGraph.psm1` (465), `scripts/dependencies/ProjectConsistency.psm1` (381), `scripts/dependencies/ConsistencyVerifier.psm1` (499), `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152), and Glob confirmation that `scripts/dependencies/BindingRedirectVerification.psm1` and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` do not yet exist. Acceptance: the four counts equal the figures in parentheses (a difference is recorded as `LINECOUNT-DRIFT:` and the run continues, because none of the four is edited) and both Globs return nothing (a hit is STOP: PREEXISTING-FILE). Artifact `evidence/baseline/p0-t9-linecount-baseline..md`. -- [ ] [P0-T10] Baseline PowerShell format step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, then CMD-POSHQC-FORMAT, then CMD-HASHSET again and `git status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies`. Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical and the porcelain over the two folders is empty (the Write Set is clean per P0-T2 and the new files do not exist yet). If a hash differs, the formatter rewrote pre-existing drift outside this item's change: record the paths as `FORMAT-DRIFT-REVERTED:`, restore each with `git checkout -- `, re-run CMD-HASHSET to confirm equality with the first set, and continue. `EXIT_CODE:` per C3. Artifact `evidence/baseline/p0-t10-poshqc-format..md`. -- [ ] [P0-T11] Baseline PowerShell analyze step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance: `ok` true with the 2-folder summary literal (the folders were analyzer-clean on 2026-09-30 per issue 929 evidence); `ok` false is STOP: ANALYZE-BASELINE-RED, because pre-existing analyzer debt in the scan scope is outside this plan and would make P2-T2 unsatisfiable. Record `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. Artifact `evidence/baseline/p0-t11-poshqc-analyze..md`. -- [ ] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines are transcribed under the CMD-JUNIT-READ long-line rule: any line the Grep tool reports as omitted is read by line number with the Read tool; whether a testsuite line is omitted depends on the execution worktree path length: at the .claude/worktrees/agent- depth a dependencies testsuite line measured 403 to 439 characters at preflight round 3, below the 501-character omission threshold). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. -- [ ] [P0-T13] Record the PowerShell budget baseline from `.claude/hooks/enforce-powershell-batch-budget.ps1`: quote lines 10-11 (caps), 14 (state file location) and 42-45 (deny behaviour); Glob `.claude/state/powershell-batch-budget.*.json` and record whether any state file exists (read-only; never opened for writing); restate the D9 stop rule in the artifact. Acceptance: the artifact quotes the three regions and carries the line `BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable`. Artifact `evidence/baseline/p0-t13-budget-baseline..md`. +- [x] [P0-T1] Read the policy and skill files in order and record the read: CLAUDE.md, `.claude/rules/general-code-change.md`, `.claude/rules/general-unit-test.md`, `.claude/rules/powershell.md`, `.claude/rules/plan-acceptance-gates.md`, `.claude/rules/tonality.md`, `.claude/skills/atomic-plan-contract/SKILL.md`, `.claude/skills/evidence-and-timestamp-conventions/SKILL.md`, `.claude/skills/acceptance-criteria-tracking/SKILL.md`, then this plan and issue.md. Artifact `evidence/baseline/phase0-instructions-read..md` with `Timestamp:`, `Policy Order:` (the numbered order above) and the explicit list of files read. Acceptance: the artifact exists and lists all eleven files. +- [x] [P0-T2] Record the baseline git anchor for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: run `git fetch origin main` (record `FETCH-EXIT:`; a non-zero value is recorded and the run continues against the local `origin/main` ref with the line `FETCH-STALE: local origin/main used`), then `git merge-base HEAD origin/main` → `BASE_SHA:`, `git rev-parse HEAD` → `P0-START:`, `git cat-file -t `, `git status --porcelain --untracked-files=all` → `BASE-PORCELAIN:` (verbatim), `git diff --name-only -- .` → `INHERITED:` (verbatim; this is the set every later footprint evaluation subtracts, C6). Acceptance: BASE_SHA is 40 hexadecimal characters and `git cat-file -t` prints `commit`; no path in BASE-PORCELAIN or INHERITED ends in `.cs`, `.csproj`, `packages.config` or `app.config`, and none lies under `scripts/dependencies/` or `tests/scripts/dependencies/` (the Write Set is clean at start); the feature folder paths and `.claude/agent-memory/` paths are expected and recorded, never asserted empty. Artifact `evidence/baseline/p0-t2-base-anchor..md`. +- [x] [P0-T3] Verify the acceptance-criteria source and mode as a baseline precondition by reading `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md`: line 9 reads `- Work Mode: minor-audit`; exactly one `## Acceptance Criteria` heading (Grep count of `^## Acceptance Criteria$` is 1); Grep count of `^- \[ \] AC[1-6]:` is 6 and of `^- \[x\] AC[1-6]:` is 0; the AC6 line contains the literal `is not measured locally`; Glob for `spec.md` and `user-story.md` in the feature folder returns nothing. Any failed condition is STOP: AC-SOURCE. Artifact `evidence/baseline/p0-t3-ac-precondition..md`. +- [x] [P0-T4] Baseline Fizzler census over `*/app.config`: Grep pattern `name="Fizzler"` with `-A 1`, glob `*/app.config`, content mode. Acceptance: 13 blocks; the following line reads `newVersion="1.3.0.0"` for exactly these 11 files at these lines — QuickFiler/app.config 51, QuickFiler.Test/app.config 47, SVGControl.Test/app.config 19, Tags/app.config 47, TaskMaster/app.config 51, TaskTree/app.config 47, TaskVisualization/app.config 47, TaskVisualization.Test/app.config 47, ToDoModel/app.config 52, ToDoModel.Test/app.config 47, UtilitiesCS.Test/app.config 47 — and reads `newVersion="1.3.1.0"` for SVGControl/app.config 15 and UtilitiesCS/app.config 52. A different member set or line is STOP: TREE-DRIFT (the Write Set line citations would be wrong). Artifact `evidence/baseline/p0-t4-fizzler-census..md`. +- [x] [P0-T5] Baseline shared-string census over `*/app.config`: Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → expected 17 occurrences across 11 files, 2 each in QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel and 1 each in QuickFiler.Test, SVGControl.Test, TaskVisualization.Test, ToDoModel.Test, UtilitiesCS.Test; Grep pattern `name="System.ClientModel"` with `-A 1` → 16 blocks (SVGControl/app.config carries none), the following line at 1.3.0.0 for exactly Tags 103, TaskVisualization 103, TaskTree 103, QuickFiler 103, TaskMaster 115, ToDoModel 108 and at 1.16.0.0 for the other 10. Acceptance: both expectations hold (otherwise STOP: TREE-DRIFT). The artifact records the per-file System.ClientModel values as `CLIENTMODEL =` lines for P1 comparison. Artifact `evidence/baseline/p0-t5-shared-string-census..md`. +- [x] [P0-T6] Baseline Unsafe census over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1`, glob `*/app.config`. Acceptance: 17 blocks and every following line reads `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; the artifact lists the 17 files. Artifact `evidence/baseline/p0-t6-unsafe-census..md`. +- [x] [P0-T7] Baseline encoding observation for the 11 Write Set configs (`QuickFiler/app.config` and the ten others of section 5): for each path run CMD-EOL (second whitespace-separated field must read `w/crlf`), CMD-BOM (record `BOM-BYTES =`; expected `239,187,191`; a different value is recorded, not a stop, and becomes the preservation reference for Phase 1), and CMD-LINECOUNT paired with Grep pattern `\r$` count mode on the same file (the two counts must be equal; expected values QuickFiler 243, QuickFiler.Test 367, SVGControl.Test 227, Tags 239, TaskMaster 430, TaskTree 239, TaskVisualization 239, TaskVisualization.Test 363, ToDoModel 323, ToDoModel.Test 363, UtilitiesCS.Test 383). Acceptance: 11 `w/crlf` lines, 11 BOM-BYTES lines, 11 equal count pairs. Artifact `evidence/baseline/p0-t7-encoding-baseline..md`. +- [x] [P0-T8] Baseline re-measurement of the known-debt table over `*/app.config` and `*/*.csproj`: (a) Grep count mode, glob `*/app.config`, pattern `assemblyIdentity name=` → 1176 total across 17 files; pattern `` and no other version is printed; (c) for each of the 3 unverifiable names, Grep glob `*/*.csproj`, pattern `Include="` → no match, and Grep glob `*/app.config`, pattern `name=""` count mode → 15, 13, 1 respectively. Acceptance: every figure equals section 7; any difference is recorded as `KNOWN-DEBT-DRIFT: ` and the run stops (D7). Completeness beyond the 18 names is proven by test 14 at P1-T15, not here. Artifact `evidence/baseline/p0-t8-known-debt-remeasure..md`. +- [x] [P0-T9] Baseline line counts (CMD-LINECOUNT) for `scripts/dependencies/PackageGraph.psm1` (465), `scripts/dependencies/ProjectConsistency.psm1` (381), `scripts/dependencies/ConsistencyVerifier.psm1` (499), `tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1` (152), and Glob confirmation that `scripts/dependencies/BindingRedirectVerification.psm1` and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` do not yet exist. Acceptance: the four counts equal the figures in parentheses (a difference is recorded as `LINECOUNT-DRIFT:` and the run continues, because none of the four is edited) and both Globs return nothing (a hit is STOP: PREEXISTING-FILE). Artifact `evidence/baseline/p0-t9-linecount-baseline..md`. +- [x] [P0-T10] Baseline PowerShell format step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, then CMD-POSHQC-FORMAT, then CMD-HASHSET again and `git status --porcelain --untracked-files=all -- scripts/dependencies tests/scripts/dependencies`. Acceptance: `ok` true with the 2-folder summary literal; the two hash sets are identical and the porcelain over the two folders is empty (the Write Set is clean per P0-T2 and the new files do not exist yet). If a hash differs, the formatter rewrote pre-existing drift outside this item's change: record the paths as `FORMAT-DRIFT-REVERTED:`, restore each with `git checkout -- `, re-run CMD-HASHSET to confirm equality with the first set, and continue. `EXIT_CODE:` per C3. Artifact `evidence/baseline/p0-t10-poshqc-format..md`. +- [x] [P0-T11] Baseline PowerShell analyze step over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance: `ok` true with the 2-folder summary literal (the folders were analyzer-clean on 2026-09-30 per issue 929 evidence); `ok` false is STOP: ANALYZE-BASELINE-RED, because pre-existing analyzer debt in the scan scope is outside this plan and would make P2-T2 unsatisfiable. Record `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count`. Artifact `evidence/baseline/p0-t11-poshqc-analyze..md`. +- [x] [P0-T12] Baseline PowerShell test step over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines are transcribed under the CMD-JUNIT-READ long-line rule: any line the Grep tool reports as omitted is read by line number with the Read tool; whether a testsuite line is omitted depends on the execution worktree path length: at the .claude/worktrees/agent- depth a dependencies testsuite line measured 403 to 439 characters at preflight round 3, below the 501-character omission threshold). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; exactly 8 JUNIT-SUITE lines with the leaf names listed under CMD-JUNIT-READ (all but BindingRedirectVerification.Tests.ps1), each `failures=0 skipped=0`, counts recorded as `BASELINE-SUITE =`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present together with the line `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`, which states the exception this plan takes to the atomic-plan-contract requirement for a numeric baseline coverage figure and its reason (the figure is read from the CI Pester job, D8). `EXIT_CODE: 0` per C3. Artifact `evidence/baseline/p0-t12-poshqc-test..md`. +- [x] [P0-T13] Record the PowerShell budget baseline from `.claude/hooks/enforce-powershell-batch-budget.ps1`: quote lines 10-11 (caps), 14 (state file location) and 42-45 (deny behaviour); Glob `.claude/state/powershell-batch-budget.*.json` and record whether any state file exists (read-only; never opened for writing); restate the D9 stop rule in the artifact. Acceptance: the artifact quotes the three regions and carries the line `BUDGET-RULE: any denied PowerShell write stops the run; no state reset, no override variable`. Artifact `evidence/baseline/p0-t13-budget-baseline..md`. ### Phase 1 — Implementation: detector module, regression tests, Fizzler sweep From 45e3f8a2e3e3455a81908bada293f1d578aeab6d Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 03:27:51 -0400 Subject: [PATCH 11/14] fix(953): sweep stale Fizzler binding redirects to 1.3.1.0 and add redirect verification gate Co-Authored-By: Claude Sonnet 5.5 --- QuickFiler.Test/app.config | 2 +- QuickFiler/app.config | 2 +- SVGControl.Test/app.config | 2 +- Tags/app.config | 2 +- TaskMaster/app.config | 2 +- TaskTree/app.config | 2 +- TaskVisualization.Test/app.config | 2 +- TaskVisualization/app.config | 2 +- ToDoModel.Test/app.config | 2 +- ToDoModel/app.config | 2 +- UtilitiesCS.Test/app.config | 2 +- .../p1-t1-module-authored.2026-10-02T03-18.md | 24 ++ ...visualization-redirect.2026-10-02T03-21.md | 20 ++ ...lization-test-redirect.2026-10-02T03-21.md | 20 ++ ...t12-todomodel-redirect.2026-10-02T03-21.md | 20 ++ ...odomodel-test-redirect.2026-10-02T03-21.md | 20 ++ ...litiescs-test-redirect.2026-10-02T03-21.md | 20 ++ ...t16-sweep-verification.2026-10-02T03-21.md | 48 +++ ...1-t17-unsafe-unchanged.2026-10-02T03-21.md | 18 + ...t4-quickfiler-redirect.2026-10-02T03-21.md | 21 ++ ...ickfiler-test-redirect.2026-10-02T03-21.md | 22 ++ ...gcontrol-test-redirect.2026-10-02T03-21.md | 20 ++ .../p1-t7-tags-redirect.2026-10-02T03-21.md | 20 ++ ...t8-taskmaster-redirect.2026-10-02T03-21.md | 20 ++ ...1-t9-tasktree-redirect.2026-10-02T03-21.md | 20 ++ .../p1-t15-pass-after.2026-10-02T03-21.md | 44 +++ .../p1-t2-tests-authored.2026-10-02T03-18.md | 26 ++ .../p1-t3-fail-before.2026-10-02T03-21.md | 61 ++++ .../plan.2026-10-02T00-16.md | 34 +- .../BindingRedirectVerification.psm1 | 139 ++++++++ .../BindingRedirectVerification.Tests.ps1 | 335 ++++++++++++++++++ 31 files changed, 946 insertions(+), 28 deletions(-) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t1-module-authored.2026-10-02T03-18.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t10-taskvisualization-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t11-taskvisualization-test-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t12-todomodel-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t13-todomodel-test-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t14-utilitiescs-test-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t16-sweep-verification.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t17-unsafe-unchanged.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t4-quickfiler-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t5-quickfiler-test-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t6-svgcontrol-test-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t7-tags-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t8-taskmaster-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t9-tasktree-redirect.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t2-tests-authored.2026-10-02T03-18.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md create mode 100644 scripts/dependencies/BindingRedirectVerification.psm1 create mode 100644 tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 diff --git a/QuickFiler.Test/app.config b/QuickFiler.Test/app.config index fce52e4d8..90a920605 100644 --- a/QuickFiler.Test/app.config +++ b/QuickFiler.Test/app.config @@ -44,7 +44,7 @@ - + diff --git a/QuickFiler/app.config b/QuickFiler/app.config index 37066ad5e..384accfea 100644 --- a/QuickFiler/app.config +++ b/QuickFiler/app.config @@ -48,7 +48,7 @@ - + diff --git a/SVGControl.Test/app.config b/SVGControl.Test/app.config index b605ab9ea..55bc634b8 100644 --- a/SVGControl.Test/app.config +++ b/SVGControl.Test/app.config @@ -16,7 +16,7 @@ - + diff --git a/Tags/app.config b/Tags/app.config index 3d537f1bd..f0c735d42 100644 --- a/Tags/app.config +++ b/Tags/app.config @@ -44,7 +44,7 @@ - + diff --git a/TaskMaster/app.config b/TaskMaster/app.config index e1feb8a28..70ac9c4fe 100644 --- a/TaskMaster/app.config +++ b/TaskMaster/app.config @@ -48,7 +48,7 @@ - + diff --git a/TaskTree/app.config b/TaskTree/app.config index 0747fc1e2..8bc054c4b 100644 --- a/TaskTree/app.config +++ b/TaskTree/app.config @@ -44,7 +44,7 @@ - + diff --git a/TaskVisualization.Test/app.config b/TaskVisualization.Test/app.config index 7f591e96b..699c6360b 100644 --- a/TaskVisualization.Test/app.config +++ b/TaskVisualization.Test/app.config @@ -44,7 +44,7 @@ - + diff --git a/TaskVisualization/app.config b/TaskVisualization/app.config index a889545bc..65555527b 100644 --- a/TaskVisualization/app.config +++ b/TaskVisualization/app.config @@ -44,7 +44,7 @@ - + diff --git a/ToDoModel.Test/app.config b/ToDoModel.Test/app.config index f860ea992..888d14e05 100644 --- a/ToDoModel.Test/app.config +++ b/ToDoModel.Test/app.config @@ -44,7 +44,7 @@ - + diff --git a/ToDoModel/app.config b/ToDoModel/app.config index c8b518062..c6e234d6f 100644 --- a/ToDoModel/app.config +++ b/ToDoModel/app.config @@ -49,7 +49,7 @@ - + diff --git a/UtilitiesCS.Test/app.config b/UtilitiesCS.Test/app.config index 2631fc5ff..f26776125 100644 --- a/UtilitiesCS.Test/app.config +++ b/UtilitiesCS.Test/app.config @@ -44,7 +44,7 @@ - + diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t1-module-authored.2026-10-02T03-18.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t1-module-authored.2026-10-02T03-18.md new file mode 100644 index 000000000..06e4a4d3c --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t1-module-authored.2026-10-02T03-18.md @@ -0,0 +1,24 @@ +# P1-T1 Module authored + +Timestamp: 2026-10-02T03-18 +Command: Write tool creating `scripts/dependencies/BindingRedirectVerification.psm1` with the section 8 content; Grep tool (path = that file, count mode) with the patterns `^`, `^Export-ModuleMember`, `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'`, `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)`, `^Set-StrictMode -Version Latest`, `[^\x00-\x7F]`, `^Import-Module `, `^Import-Module .*Force` +EXIT_CODE: 0 + +Observations (each Grep passed the absolute file path of the item worktree, recorded here as `/scripts/dependencies/BindingRedirectVerification.psm1`): + +```text +LINECOUNT BindingRedirectVerification.psm1 = 139 (required 90..200; target 100..150 observed) +Export-ModuleMember (^Export-ModuleMember) count 1 +export names ('ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect') count 2 +PackageGraph import (Import-Module (Join-Path $PSScriptRoot 'PackageGraph.psm1')) count 1 +^Set-StrictMode -Version Latest count 1 +non-ASCII ([^\x00-\x7F]) count 0 +^Import-Module count 1 +^Import-Module .*Force count 0 +``` + +The Write tool was not denied by the PowerShell batch-budget hook (production slot 1 of the item's 1 used; no BUDGET-DENIED). + +Acceptance: file exists; line count 139 is within 90..200; all pattern counts equal the plan's expected values. + +Output Summary: BindingRedirectVerification.psm1 created with 139 lines; two exported functions, one Import-Module without -Force, StrictMode set, ASCII-only. All P1-T1 acceptance counts hold. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t10-taskvisualization-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t10-taskvisualization-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..55af618e6 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t10-taskvisualization-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T10 EDIT-FIZZLER TaskVisualization/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `TaskVisualization/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /TaskVisualization/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 TaskVisualization/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto TaskVisualization/app.config +BOM-BYTES TaskVisualization/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 102-103: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.3.0.0. + +Output Summary: One-line Fizzler redirect edit applied to TaskVisualization/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t11-taskvisualization-test-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t11-taskvisualization-test-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..358462974 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t11-taskvisualization-test-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T11 EDIT-FIZZLER TaskVisualization.Test/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `TaskVisualization.Test/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /TaskVisualization.Test/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 TaskVisualization.Test/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto TaskVisualization.Test/app.config +BOM-BYTES TaskVisualization.Test/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 102-103: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.16.0.0. + +Output Summary: One-line Fizzler redirect edit applied to TaskVisualization.Test/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t12-todomodel-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t12-todomodel-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..89a30d2c1 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t12-todomodel-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T12 EDIT-FIZZLER ToDoModel/app.config (lines 51-52) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `ToDoModel/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /ToDoModel/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 ToDoModel/app.config +DIFF - (diff -U0 hunk @@ -52 +52 @@) + + +EOL i/lf w/crlf attr/text=auto ToDoModel/app.config +BOM-BYTES ToDoModel/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 51: + 52: +ClientModel 107-108: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.3.0.0. + +Output Summary: One-line Fizzler redirect edit applied to ToDoModel/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t13-todomodel-test-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t13-todomodel-test-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..8b82756b5 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t13-todomodel-test-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T13 EDIT-FIZZLER ToDoModel.Test/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `ToDoModel.Test/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /ToDoModel.Test/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 ToDoModel.Test/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto ToDoModel.Test/app.config +BOM-BYTES ToDoModel.Test/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 102-103: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.16.0.0. + +Output Summary: One-line Fizzler redirect edit applied to ToDoModel.Test/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t14-utilitiescs-test-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t14-utilitiescs-test-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..dc48679cc --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t14-utilitiescs-test-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T14 EDIT-FIZZLER UtilitiesCS.Test/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `UtilitiesCS.Test/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /UtilitiesCS.Test/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 UtilitiesCS.Test/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto UtilitiesCS.Test/app.config +BOM-BYTES UtilitiesCS.Test/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 122-123: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.16.0.0. + +Output Summary: One-line Fizzler redirect edit applied to UtilitiesCS.Test/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t16-sweep-verification.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t16-sweep-verification.2026-10-02T03-21.md new file mode 100644 index 000000000..e67b5c0a3 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t16-sweep-verification.2026-10-02T03-21.md @@ -0,0 +1,48 @@ +# P1-T16 Sweep verification over app.config files (AC1 evidence) + +Timestamp: 2026-10-02T03-21 +Command: Grep `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` (count mode) and Grep `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` (count mode), glob `**/app.config`, path `` (glob substitution recorded at P0-T4: `*/app.config` returns no match at this worktree depth; `**/app.config` returns the same 17 root-level files); Grep `name="Fizzler"` -A 1, same glob and path; git -C diff --name-only 860d67bf4fddecb929e0d6c166065fd1ee752feb -- '*/app.config'; git -C status --porcelain -- '*/app.config' +EXIT_CODE: 0 + +```text +COUNT 1.3.1.0 redirect (oldVersion 0.0.0.0-1.3.1.0 / newVersion 1.3.1.0): 13 occurrences across 13 files + QuickFiler, QuickFiler.Test, SVGControl, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, + TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS, UtilitiesCS.Test (1 each) +COUNT 1.3.0.0 redirect (oldVersion 0.0.0.0-1.3.0.0 / newVersion 1.3.0.0): 6 occurrences across 6 files + QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel (1 each; the System.ClientModel redirect) +FIZZLER BLOCKS: 13 blocks, every following line reads oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0" +``` + +```text +git diff --name-only -- '*/app.config' (11 paths): +QuickFiler.Test/app.config +QuickFiler/app.config +SVGControl.Test/app.config +Tags/app.config +TaskMaster/app.config +TaskTree/app.config +TaskVisualization.Test/app.config +TaskVisualization/app.config +ToDoModel.Test/app.config +ToDoModel/app.config +UtilitiesCS.Test/app.config + +git status --porcelain -- '*/app.config' (11 lines): + M QuickFiler.Test/app.config + M QuickFiler/app.config + M SVGControl.Test/app.config + M Tags/app.config + M TaskMaster/app.config + M TaskTree/app.config + M TaskVisualization.Test/app.config + M TaskVisualization/app.config + M ToDoModel.Test/app.config + M ToDoModel/app.config + M UtilitiesCS.Test/app.config +``` + +The pathspec was passed unquoted from the Bash tool; the shell found no match for the glob in its own working directory and passed it to git literally, so git applied the pathspec glob. The 11 listed paths equal the 11 Write Set config paths of section 5 and the porcelain lines are exactly 11 ` M` lines for the same paths. + +Acceptance: all four observations hold (13 across 13; exactly 6 across exactly the six named files; 13 Fizzler blocks at 1.3.1.0 in both attributes; diff and porcelain agree on exactly the 11 Write Set config paths). + +Output Summary: Sweep verified. 13 Fizzler redirects at 1.3.1.0 across 13 files; the 6 remaining 1.3.0.0 strings are the System.ClientModel redirects; 11 config files modified, matching the Write Set. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t17-unsafe-unchanged.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t17-unsafe-unchanged.2026-10-02T03-21.md new file mode 100644 index 000000000..b650314c4 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t17-unsafe-unchanged.2026-10-02T03-21.md @@ -0,0 +1,18 @@ +# P1-T17 Unsafe re-verification after the edits (AC2 evidence) + +Timestamp: 2026-10-02T03-21 +Command: Grep `name="System.Runtime.CompilerServices.Unsafe"` -A 1 (content mode, no head limit), glob `**/app.config`, path `` (glob substitution recorded at P0-T4); git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- '*/app.config' +EXIT_CODE: 0 + +```text +UNSAFE BLOCKS: 17, every following line reads oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" +Files (17): QuickFiler, QuickFiler.Test, SVGControl, SVGControl.Test, Tags, Tags.Test, TaskMaster, TaskMaster.Test, + TaskTree, TaskTree.Test, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS, + UtilitiesCS.Test, VBFunctions.Test +``` + +The diff of the app.config files against BASE_SHA contains 22 content lines: 11 removed lines, each ` `, and 11 added lines, each ` `, one pair per file in the 11 Write Set configs (hunks at QuickFiler.Test 47, QuickFiler 51, SVGControl.Test 19, Tags 47, TaskMaster 51, TaskTree 47, TaskVisualization.Test 47, TaskVisualization 47, ToDoModel.Test 47, ToDoModel 52, UtilitiesCS.Test 47). No content line contains `Unsafe`. The diff was taken with `-U0`, which removes context lines only; the content lines are the same as with default context. + +Acceptance: both hold. 17 Unsafe blocks unchanged at 6.0.3.0; the 22 diff content lines are exactly the 11 removed and 11 added Fizzler redirect lines and none contains `Unsafe`. + +Output Summary: Unsafe redirects unchanged. 17 blocks all at 6.0.3.0; the app.config diff against BASE_SHA holds only the 11 Fizzler redirect line replacements (22 content lines), none mentioning Unsafe. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t4-quickfiler-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t4-quickfiler-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..6f72d0c8d --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t4-quickfiler-redirect.2026-10-02T03-21.md @@ -0,0 +1,21 @@ +# P1-T4 EDIT-FIZZLER QuickFiler/app.config (lines 50-51) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `QuickFiler/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- QuickFiler/app.config; git -C diff 860d67bf4fddecb929e0d6c166065fd1ee752feb -- QuickFiler/app.config; git -C ls-files --eol -- QuickFiler/app.config; pwsh -NoProfile -Command '(Get-Content -LiteralPath /QuickFiler/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 on the file +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 QuickFiler/app.config +DIFF - + + +EOL i/lf w/crlf attr/text=auto QuickFiler/app.config +BOM-BYTES QuickFiler/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 50: + 51: +ClientModel 102: + 103: (unchanged from P0-T5) +``` + +Acceptance (all six): (1) numstat `1 1`; (2) one `-` and one `+` content line with the required text; (3) `w/crlf`; (4) BOM bytes equal the P0-T7 value; (5) Fizzler redirect reads 1.3.1.0 in both attributes; (6) System.ClientModel redirect unchanged at 1.3.0.0. + +Output Summary: One-line Fizzler redirect edit applied to QuickFiler/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t5-quickfiler-test-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t5-quickfiler-test-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..e3f4f44ed --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t5-quickfiler-test-redirect.2026-10-02T03-21.md @@ -0,0 +1,22 @@ +# P1-T5 EDIT-FIZZLER QuickFiler.Test/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `QuickFiler.Test/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /QuickFiler.Test/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 QuickFiler.Test/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto QuickFiler.Test/app.config +BOM-BYTES QuickFiler.Test/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 102-103: (unchanged from P0-T5) +``` + +The diff was taken with `-U0` (no context lines) so that the ten files' content lines read compactly; the content lines are the same as the default-context diff. Hunk text for this file: exactly one `-` content line and exactly one `+` content line with the required text. + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.16.0.0. + +Output Summary: One-line Fizzler redirect edit applied to QuickFiler.Test/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t6-svgcontrol-test-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t6-svgcontrol-test-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..a8833073a --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t6-svgcontrol-test-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T6 EDIT-FIZZLER SVGControl.Test/app.config (lines 18-19) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `SVGControl.Test/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /SVGControl.Test/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 SVGControl.Test/app.config +DIFF - (diff -U0 hunk @@ -19 +19 @@) + + +EOL i/lf w/crlf attr/text=auto SVGControl.Test/app.config +BOM-BYTES SVGControl.Test/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 18: + 19: +ClientModel 46-47: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.16.0.0. + +Output Summary: One-line Fizzler redirect edit applied to SVGControl.Test/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t7-tags-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t7-tags-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..353a96932 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t7-tags-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T7 EDIT-FIZZLER Tags/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `Tags/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /Tags/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 Tags/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto Tags/app.config +BOM-BYTES Tags/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 102-103: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.3.0.0. + +Output Summary: One-line Fizzler redirect edit applied to Tags/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t8-taskmaster-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t8-taskmaster-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..28d7524cd --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t8-taskmaster-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T8 EDIT-FIZZLER TaskMaster/app.config (lines 50-51) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `TaskMaster/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /TaskMaster/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 TaskMaster/app.config +DIFF - (diff -U0 hunk @@ -51 +51 @@) + + +EOL i/lf w/crlf attr/text=auto TaskMaster/app.config +BOM-BYTES TaskMaster/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 50: + 51: +ClientModel 114-115: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.3.0.0. + +Output Summary: One-line Fizzler redirect edit applied to TaskMaster/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t9-tasktree-redirect.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t9-tasktree-redirect.2026-10-02T03-21.md new file mode 100644 index 000000000..d034edec0 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t9-tasktree-redirect.2026-10-02T03-21.md @@ -0,0 +1,20 @@ +# P1-T9 EDIT-FIZZLER TaskTree/app.config (lines 46-47) + +Timestamp: 2026-10-02T03-21 +Command: Edit tool on `TaskTree/app.config` (two-line old_string, identity line plus redirect line); git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C diff -U0 860d67bf4fddecb929e0d6c166065fd1ee752feb -- ; git -C ls-files --eol -- ; pwsh -NoProfile -Command '(Get-Content -LiteralPath /TaskTree/app.config -AsByteStream -TotalCount 3) -join [char]44'; Grep `name="Fizzler"` -A 1 and Grep `name="System.ClientModel"` -A 1 (glob `**/app.config`, path ``; the 17-file result is read for this path) +EXIT_CODE: 0 + +```text +NUMSTAT 1 1 TaskTree/app.config +DIFF - (diff -U0 hunk @@ -47 +47 @@) + + +EOL i/lf w/crlf attr/text=auto TaskTree/app.config +BOM-BYTES TaskTree/app.config=239,187,191 (P0-T7 reference 239,187,191; equal) +Fizzler 46: + 47: +ClientModel 102-103: (unchanged from P0-T5) +``` + +Acceptance (all six): numstat `1 1`; one `-` and one `+` content line with the required text; `w/crlf`; BOM bytes equal P0-T7; Fizzler 1.3.1.0 in both attributes; System.ClientModel unchanged at 1.3.0.0. + +Output Summary: One-line Fizzler redirect edit applied to TaskTree/app.config; CRLF and BOM preserved; System.ClientModel redirect unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md new file mode 100644 index 000000000..3a8a66886 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md @@ -0,0 +1,44 @@ +# P1-T15 Suite run after the config edits (AC5 pass-after evidence; AC4 evidence through test 14) + +Timestamp: 2026-10-02T03-21 +Command: pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue' (CMD-JUNIT-DELETE); MCP mcp__drm-copilot__run_poshqc_test with workspace_root `` and scan_folders `["tests/scripts/dependencies"]`; CMD-JUNIT-READ Greps (`/artifacts/pester/pester-junit.xml` +EXIT_CODE: 0 +ExpectedExitCode: 0 + +CMD-JUNIT-DELETE: the pwsh invocation printed nothing and reported no error. GLOB-BLIND: the Glob tool does not return this gitignored file (recorded at P0-T12), so the absence check is not discriminating. The JUnit root and testsuite elements in this worktree's document carry no `timestamp` attribute, so the timestamp comparison named in the delegation is not available; freshness is shown instead by the document content differing from the P1-T3 document read just before the delete: JUNIT-ROOT failures 0 now against 2 at P1-T3 and root `time` 5.460 against 5.998. A document surviving the delete could not report both changes. + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_test","workspace_root":"","summary":"Ran bundled PoshQC test against '' with 1 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): payload `ok` true and JUNIT root `failures=0`, so 0. + +CMD-JUNIT-READ: + +```text +JUNIT-ROOT tests=151 failures=0 errors=0 disabled=0 +JUNIT-SUITE AnalyzerItemRepair.Tests.ps1 tests=13 failures=0 skipped=0 +JUNIT-SUITE BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0 +JUNIT-SUITE ConsistencyVerifier.Tests.ps1 tests=14 failures=0 skipped=0 +JUNIT-SUITE DependabotConfig.Tests.ps1 tests=17 failures=0 skipped=0 +JUNIT-SUITE PackageCompatibility.Tests.ps1 tests=8 failures=0 skipped=0 +JUNIT-SUITE PackageGraph.Tests.ps1 tests=32 failures=0 skipped=0 +JUNIT-SUITE ProjectConsistency.Tests.ps1 tests=18 failures=0 skipped=0 +JUNIT-SUITE Repair-PackageManifestConsistency.Tests.ps1 tests=31 failures=0 skipped=0 +JUNIT-SUITE RepositoryTreeConsistency.Tests.ps1 tests=4 failures=0 skipped=0 +JUNIT-NOTPASSED: none +``` + +The `/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`): + +```text +It blocks (^\s*It ') count 14 +each of the 14 It names of section 9 (escaped, per name) count 1 each (14 of 14) +LINECOUNT BindingRedirectVerification.Tests.ps1 = 335 (below 500) +forbidden file APIs ($TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile) count 0 +non-ASCII ([^\x00-\x7F]) count 0 +PackageGraph import line with -Force (\r?$ form) count 1 +BindingRedirectVerification import line with -Force count 1 +^\s*Import-Module count 2 (line 5 PackageGraph, line 6 BindingRedirectVerification) +``` + +The It names were matched by escaped regular expressions because the Grep tool has no fixed-string option; the only metacharacter in the names is `.`, escaped in every pattern. + +The Write tool was not denied by the PowerShell batch-budget hook (test slot 1 of the item's 1 used; no BUDGET-DENIED). + +Acceptance: file exists; 14 It blocks; every section 9 It name present once; 335 lines (< 500); no temporary-file API; ASCII-only; both import lines present, PackageGraph first. + +Output Summary: BindingRedirectVerification.Tests.ps1 created with 335 lines and 14 It blocks matching the section 9 names; no temporary-file API, ASCII-only, and the two -Force imports are in the required order. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md new file mode 100644 index 000000000..663600614 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md @@ -0,0 +1,61 @@ +# P1-T3 [expect-fail] Suite run before the config edits (AC5 fail-before evidence) + +Timestamp: 2026-10-02T03-21 +Command: pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue' (CMD-JUNIT-DELETE); MCP mcp__drm-copilot__run_poshqc_test with workspace_root `` and scan_folders `["tests/scripts/dependencies"]`; CMD-JUNIT-READ Greps (`/artifacts/pester/pester-junit.xml`, then the Read tool at the two failed testcase lines +EXIT_CODE: 1 +ExpectedExitCode: 1 + +CMD-JUNIT-DELETE: the pwsh invocation printed nothing and the Bash tool reported no exit-code error (the document did exist from the Phase 0 run, so the delete had an effect). GLOB-BLIND: the Glob tool does not return this gitignored file (recorded at P0-T12), so the absence check is not discriminating; freshness is shown by the document content, which carries a `BindingRedirectVerification.Tests.ps1` suite that did not exist at the P0-T12 run (9 suites, 151 tests now against 8 suites, 137 tests then). + +Payload (verbatim, worktree root replaced per C4; the tool returned it as an error result): + +```text +{"ok": false, "tool": "run_poshqc_test", "workspace_root": "", "summary": "Command exited with code 2."} +``` + +EXIT_CODE derivation (C3): payload `ok` false and JUNIT root `failures=2`, so 1. The tool's own exit code 2 is the count of failed tests as the runner reported it and is recorded as an observation, not as this artifact's EXIT_CODE. + +CMD-JUNIT-READ: + +```text +JUNIT-ROOT tests=151 failures=2 errors=0 disabled=0 +JUNIT-SUITE AnalyzerItemRepair.Tests.ps1 tests=13 failures=0 skipped=0 +JUNIT-SUITE BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0 +JUNIT-SUITE ConsistencyVerifier.Tests.ps1 tests=14 failures=0 skipped=0 +JUNIT-SUITE DependabotConfig.Tests.ps1 tests=17 failures=0 skipped=0 +JUNIT-SUITE PackageCompatibility.Tests.ps1 tests=8 failures=0 skipped=0 +JUNIT-SUITE PackageGraph.Tests.ps1 tests=32 failures=0 skipped=0 +JUNIT-SUITE ProjectConsistency.Tests.ps1 tests=18 failures=0 skipped=0 +JUNIT-SUITE Repair-PackageManifestConsistency.Tests.ps1 tests=31 failures=0 skipped=0 +JUNIT-SUITE RepositoryTreeConsistency.Tests.ps1 tests=4 failures=0 skipped=0 +JUNIT-NOTPASSED Repository binding redirects (issue 953).names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files +JUNIT-NOTPASSED Repository binding redirects (issue 953).reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference +``` + +The `` correction was needed. + +Other suites equal their P0-T12 BASELINE-SUITE counts with `failures=0`: AnalyzerItemRepair 13, ConsistencyVerifier 14, DependabotConfig 17, PackageCompatibility 8, PackageGraph 32, ProjectConsistency 18, Repair-PackageManifestConsistency 31, RepositoryTreeConsistency 4. + +COVERAGE-MEASUREMENT: deferred to the CI Pester job (_pester.yml); no local figure + +Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT failures=2; exactly two JUNIT-NOTPASSED lines (tests 13 and 14 by name); test 13 and test 14 messages carry the required content; every other suite equals its baseline. + +Output Summary: Red state observed as required: 151 tests, 2 failures (test 13 `but got 11` over the eleven stale configs; test 14 observed list of 16 entries including `Fizzler|1.3.0.0`). Tests 1 to 12 pass. Eight pre-existing suites unchanged from baseline. This is the AC5 fail-before evidence. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index 3f9975a02..068abdf90 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -354,23 +354,23 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on ### Phase 1 — Implementation: detector module, regression tests, Fizzler sweep -- [ ] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `^Import-Module ` count 1 and Grep pattern `^Import-Module .*Force` count 0 (the import statement carries no -Force; the explanatory comment at the top of the module mentions -Force and is not gated). A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. -- [ ] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/PackageGraph\.psm1'\) -Force\r?$` count 1 and Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/BindingRedirectVerification\.psm1'\) -Force\r?$` count 1 (the two section 9 import lines; the `\r?` admits a carriage return before the line end, because ripgrep's `$` does not match before a carriage return and the terminator the Write tool and the PoshQC formatter leave on this new file is not pinned by this plan; the gate asserts the line content, not the terminator), Grep pattern `^\s*Import-Module ` count 2, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. -- [ ] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (the two JUNIT-MESSAGE lines below are transcribed from the Read tool under the CMD-JUNIT-READ long-line rule as CMD-JUNIT-READ (c) requires for every not-passed testcase, whatever the line length; the Grep `-n` output supplies the line numbers the Read tool is pointed at). Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12, or a failure of test 13 or 14 whose JUNIT-MESSAGE does not begin `Expected ` and contain `, but got ` (an exception or StrictMode error inside the test body rather than the expected assertion failure), is a defect in a new file: fix the file concerned, record the correction, and re-run this task as `.iter`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. -- [ ] [P1-T4] EDIT-FIZZLER `QuickFiler/app.config` lines 50-51; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t4-quickfiler-redirect..md`. -- [ ] [P1-T5] EDIT-FIZZLER `QuickFiler.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t5-quickfiler-test-redirect..md`. -- [ ] [P1-T6] EDIT-FIZZLER `SVGControl.Test/app.config` lines 18-19; System.ClientModel at 46-47 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t6-svgcontrol-test-redirect..md`. -- [ ] [P1-T7] EDIT-FIZZLER `Tags/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t7-tags-redirect..md`. -- [ ] [P1-T8] EDIT-FIZZLER `TaskMaster/app.config` lines 50-51; System.ClientModel at 114-115 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t8-taskmaster-redirect..md`. -- [ ] [P1-T9] EDIT-FIZZLER `TaskTree/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t9-tasktree-redirect..md`. -- [ ] [P1-T10] EDIT-FIZZLER `TaskVisualization/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t10-taskvisualization-redirect..md`. -- [ ] [P1-T11] EDIT-FIZZLER `TaskVisualization.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t11-taskvisualization-test-redirect..md`. -- [ ] [P1-T12] EDIT-FIZZLER `ToDoModel/app.config` lines 51-52; System.ClientModel at 107-108 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t12-todomodel-redirect..md`. -- [ ] [P1-T13] EDIT-FIZZLER `ToDoModel.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t13-todomodel-test-redirect..md`. -- [ ] [P1-T14] EDIT-FIZZLER `UtilitiesCS.Test/app.config` lines 46-47; System.ClientModel at 122-123 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t14-utilitiescs-test-redirect..md`. -- [ ] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. -- [ ] [P1-T16] Sweep verification over `*/app.config` (AC1 evidence): Grep pattern `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` glob `*/app.config` count mode → 13 occurrences across 13 files; Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → exactly 6 occurrences across exactly QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; Grep pattern `name="Fizzler"` with `-A 1` → 13 blocks all followed by `1.3.1.0` in both attributes; `git diff --name-only -- '*/app.config'` lists exactly the 11 Write Set config paths, paired with `git status --porcelain -- '*/app.config'` showing exactly 11 ` M` lines for the same paths. Acceptance: all four observations hold. Artifact `evidence/qa-gates/p1-t16-sweep-verification..md`. -- [ ] [P1-T17] Unsafe re-verification after the edits over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1` → 17 blocks all `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; `git diff -- '*/app.config'` contains no content line (beginning `-` or `+` after the header pairs) that contains `Unsafe`, and its 22 content lines are the 11 removed and 11 added Fizzler redirect lines. Acceptance: both hold. Artifact `evidence/qa-gates/p1-t17-unsafe-unchanged..md`. +- [x] [P1-T1] Author `scripts/dependencies/BindingRedirectVerification.psm1` with the content of section 8 (Write tool). Acceptance: the file exists; CMD-LINECOUNT is at least 90 and at most 200 (record the value; the 100-150 target is an observation); Grep pattern `^Export-ModuleMember` count 1 and Grep pattern `'ConvertTo-ReferenceVersionMap'|'Find-StaleBindingRedirect'` count 2 within the file; Grep pattern `Import-Module \(Join-Path \$PSScriptRoot 'PackageGraph.psm1'\)` count 1; Grep pattern `^Set-StrictMode -Version Latest` count 1; Grep pattern `[^\x00-\x7F]` count 0 (ASCII-only); Grep pattern `^Import-Module ` count 1 and Grep pattern `^Import-Module .*Force` count 0 (the import statement carries no -Force; the explanatory comment at the top of the module mentions -Force and is not gated). A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/qa-gates/p1-t1-module-authored..md`. +- [x] [P1-T2] Author `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` per section 9 (Write tool). Acceptance: the file exists; Grep pattern `^\s*It '` count 14 and each of the 14 It names of section 9 is present verbatim (Grep with `-F` per name, count 1 each); CMD-LINECOUNT below 500; Grep pattern `\$TestDrive|New-Item|Set-Content|Out-File|Add-Content|New-TemporaryFile` count 0; Grep pattern `[^\x00-\x7F]` count 0; Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/PackageGraph\.psm1'\) -Force\r?$` count 1 and Grep pattern `^\s*Import-Module \(Join-Path \$script:RepoRoot 'scripts/dependencies/BindingRedirectVerification\.psm1'\) -Force\r?$` count 1 (the two section 9 import lines; the `\r?` admits a carriage return before the line end, because ripgrep's `$` does not match before a carriage return and the terminator the Write tool and the PoshQC formatter leave on this new file is not pinned by this plan; the gate asserts the line content, not the terminator), Grep pattern `^\s*Import-Module ` count 2, the PackageGraph import on the earlier line. A denied Write is STOP: BUDGET-DENIED (D9). Artifact `evidence/regression-testing/p1-t2-tests-authored..md`. +- [x] [P1-T3] [expect-fail] Run the suite before the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (the two JUNIT-MESSAGE lines below are transcribed from the Read tool under the CMD-JUNIT-READ long-line rule as CMD-JUNIT-READ (c) requires for every not-passed testcase, whatever the line length; the Grep `-n` output supplies the line numbers the Read tool is pointed at). Acceptance: JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=2 skipped=0`; JUNIT-ROOT `failures=2`; exactly two JUNIT-NOTPASSED lines whose names end with test 13's name `names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files` and test 14's name `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`; test 13's JUNIT-MESSAGE contains `but got 11` and `1.3.0.0` and each of the eleven directory names QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test; test 14's JUNIT-MESSAGE contains `Fizzler|1.3.0.0`, and its text between `observed: ` and the following `, but got` lists exactly 16 `;`-separated entries: the 15 section 7 pairs plus `Fizzler|1.3.0.0`; any other entry is STOP: KNOWN-DEBT-DRIFT before P1-T4; every other suite line equals its P0-T12 `BASELINE-SUITE` count with `failures=0`. `EXIT_CODE: 1` with `ExpectedExitCode: 1` (C3: `ok` false or root failures above 0). A failure among tests 1 to 12, or a failure of test 13 or 14 whose JUNIT-MESSAGE does not begin `Expected ` and contain `, but got ` (an exception or StrictMode error inside the test body rather than the expected assertion failure), is a defect in a new file: fix the file concerned, record the correction, and re-run this task as `.iter`; the acceptance above must hold on the final iteration. This artifact is the AC5 fail-before evidence. Artifact `evidence/regression-testing/p1-t3-fail-before..md`. +- [x] [P1-T4] EDIT-FIZZLER `QuickFiler/app.config` lines 50-51; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t4-quickfiler-redirect..md`. +- [x] [P1-T5] EDIT-FIZZLER `QuickFiler.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t5-quickfiler-test-redirect..md`. +- [x] [P1-T6] EDIT-FIZZLER `SVGControl.Test/app.config` lines 18-19; System.ClientModel at 46-47 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t6-svgcontrol-test-redirect..md`. +- [x] [P1-T7] EDIT-FIZZLER `Tags/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t7-tags-redirect..md`. +- [x] [P1-T8] EDIT-FIZZLER `TaskMaster/app.config` lines 50-51; System.ClientModel at 114-115 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t8-taskmaster-redirect..md`. +- [x] [P1-T9] EDIT-FIZZLER `TaskTree/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t9-tasktree-redirect..md`. +- [x] [P1-T10] EDIT-FIZZLER `TaskVisualization/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t10-taskvisualization-redirect..md`. +- [x] [P1-T11] EDIT-FIZZLER `TaskVisualization.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t11-taskvisualization-test-redirect..md`. +- [x] [P1-T12] EDIT-FIZZLER `ToDoModel/app.config` lines 51-52; System.ClientModel at 107-108 stays 1.3.0.0. Artifact `evidence/qa-gates/p1-t12-todomodel-redirect..md`. +- [x] [P1-T13] EDIT-FIZZLER `ToDoModel.Test/app.config` lines 46-47; System.ClientModel at 102-103 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t13-todomodel-test-redirect..md`. +- [x] [P1-T14] EDIT-FIZZLER `UtilitiesCS.Test/app.config` lines 46-47; System.ClientModel at 122-123 stays 1.16.0.0. Artifact `evidence/qa-gates/p1-t14-utilitiescs-test-redirect..md`. +- [x] [P1-T15] Run the suite after the config edits over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance: `ok` true; JUNIT-ROOT `failures=0`; JUNIT-SUITE `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; `RepositoryTreeConsistency.Tests.ps1` and every other suite equal their P0-T12 `BASELINE-SUITE` counts with `failures=0`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present. `EXIT_CODE: 0`. This artifact is the AC5 pass-after evidence and, through test 14, the AC4 evidence; a red test 14 here means the known-debt set has a member P0-T8 did not measure: STOP: KNOWN-DEBT-DRIFT with the JUNIT-MESSAGE recorded. Artifact `evidence/regression-testing/p1-t15-pass-after..md`. +- [x] [P1-T16] Sweep verification over `*/app.config` (AC1 evidence): Grep pattern `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` glob `*/app.config` count mode → 13 occurrences across 13 files; Grep pattern `oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0"` glob `*/app.config` count mode → exactly 6 occurrences across exactly QuickFiler, Tags, TaskMaster, TaskTree, TaskVisualization, ToDoModel; Grep pattern `name="Fizzler"` with `-A 1` → 13 blocks all followed by `1.3.1.0` in both attributes; `git diff --name-only -- '*/app.config'` lists exactly the 11 Write Set config paths, paired with `git status --porcelain -- '*/app.config'` showing exactly 11 ` M` lines for the same paths. Acceptance: all four observations hold. Artifact `evidence/qa-gates/p1-t16-sweep-verification..md`. +- [x] [P1-T17] Unsafe re-verification after the edits over `*/app.config` (AC2 evidence): Grep pattern `name="System.Runtime.CompilerServices.Unsafe"` with `-A 1` → 17 blocks all `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`; `git diff -- '*/app.config'` contains no content line (beginning `-` or `+` after the header pairs) that contains `Unsafe`, and its 22 content lines are the 11 removed and 11 added Fizzler redirect lines. Acceptance: both hold. Artifact `evidence/qa-gates/p1-t17-unsafe-unchanged..md`. ### Phase 2 — Final QC loop, footprint gate, acceptance check-off and reduced audit diff --git a/scripts/dependencies/BindingRedirectVerification.psm1 b/scripts/dependencies/BindingRedirectVerification.psm1 new file mode 100644 index 000000000..8c1539be3 --- /dev/null +++ b/scripts/dependencies/BindingRedirectVerification.psm1 @@ -0,0 +1,139 @@ +<# +.SYNOPSIS + Detects application-configuration binding redirects whose newVersion names an assembly + version that no project file Reference declares. + +.DESCRIPTION + BindingRedirectVerification is a detection layer beside the dependency-consistency + tooling for issue #911: PackageGraph parses, ProjectConsistency reconciles and + ConsistencyVerifier detects manifest and project-file faults. This module carries the + one rule issue #953 adds. A bindingRedirect newVersion must equal a version that some + project file declares in a Reference Include for the same assembly name, because that is + the assembly version the build copies to the output directory. Package versions are not + consulted: a package version and its assembly version are different quantities. + + Both functions are pure over text. The deployed-version source is an injected + scriptblock, so the detector runs in memory with no file dependency; the repository-level + test supplies a provider built from every project file. + + Exported functions: + - ConvertTo-ReferenceVersionMap + - Find-StaleBindingRedirect + #> + +Set-StrictMode -Version Latest + +<# Imported without -Force deliberately, as ProjectConsistency.psm1 does: a nested + Import-Module -Force removes the module from the whole session before re-importing it, + which would strip the parser from a caller that had already imported it. #> +Import-Module (Join-Path $PSScriptRoot 'PackageGraph.psm1') + +function ConvertTo-ReferenceVersionMap { + <# + .SYNOPSIS + Builds a map from assembly name to the versions the supplied project files declare + in Reference Include attributes. + .DESCRIPTION + Only an Include of the form "Name, Version=X.Y.Z.W, ..." contributes. A Reference + without a Version is not evidence of a deployed version and is skipped. Versions are + unioned across every supplied text, so an assembly two projects reference at two + versions maps to both. An empty or whitespace-only element is accepted at parameter + binding and then rejected by ConvertFrom-ProjectFileText, which throws for it. + .PARAMETER ProjectText + The project-file texts. An empty collection yields an empty map. + #> + [CmdletBinding()] + [OutputType([hashtable])] + param( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [AllowEmptyString()] + [string[]]$ProjectText + ) + + $versions = @{} + foreach ($text in $ProjectText) { + $reference = @(ConvertFrom-ProjectFileText -Text $text | Where-Object { $_.Kind -eq 'Reference' }) + foreach ($record in $reference) { + $match = [regex]::Match($record.Value, '^\s*(?[^,]+?)\s*,\s*Version=(?[^,\s]+)') + if (-not $match.Success) { continue } + $name = $match.Groups['name'].Value + if (-not $versions.ContainsKey($name)) { + $versions[$name] = [System.Collections.Generic.List[string]]::new() + } + $value = $match.Groups['version'].Value + if (-not $versions[$name].Contains($value)) { $versions[$name].Add($value) } + } + } + + $map = @{} + foreach ($key in $versions.Keys) { $map[$key] = [string[]]$versions[$key].ToArray() } + return $map +} + +function Find-StaleBindingRedirect { + <# + .SYNOPSIS + Reports every bindingRedirect whose newVersion equals no deployed version of its + assembly, with the examined-entry count and the unverifiable assembly names. + .DESCRIPTION + A dependentAssembly block with no bindingRedirect is not examined. An assembly for + which the provider returns no version is listed as unverifiable and is not a finding, + so a redirect for a transitively deployed assembly needs no allow list. Only + newVersion is compared; the oldVersion range is a request filter, not a deployment + claim. Empty or whitespace text examines zero entries. Text that is not an + application configuration document is rejected by the parser. + .PARAMETER AppConfigText + The application configuration text. + .PARAMETER DeployedVersionProvider + A delegate taking an assembly name and returning its deployed version strings, or + nothing when the name is unknown. + #> + [CmdletBinding()] + [OutputType([pscustomobject])] + param( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string]$AppConfigText, + + [Parameter(Mandatory = $true)] + [scriptblock]$DeployedVersionProvider + ) + + $finding = [System.Collections.Generic.List[pscustomobject]]::new() + $unverifiable = [System.Collections.Generic.List[string]]::new() + $examined = 0 + + if (-not [string]::IsNullOrWhiteSpace($AppConfigText)) { + foreach ($record in @(ConvertFrom-AppConfigText -Text $AppConfigText)) { + if ([string]::IsNullOrEmpty($record.NewVersion)) { continue } + $examined++ + $deployed = @(& $DeployedVersionProvider $record.Name | + Where-Object { -not [string]::IsNullOrEmpty([string]$_) } | + ForEach-Object { [string]$_ }) + if ($deployed.Count -eq 0) { + if (-not $unverifiable.Contains($record.Name)) { $unverifiable.Add($record.Name) } + continue + } + if ($deployed -contains $record.NewVersion) { continue } + $finding.Add([pscustomobject]@{ + PSTypeName = 'BindingRedirectVerification.StaleRedirect' + AssemblyName = $record.Name + NewVersion = $record.NewVersion + DeployedVersions = [string[]]$deployed + }) + } + } + + return [pscustomobject]@{ + PSTypeName = 'BindingRedirectVerification.DetectionResult' + Finding = $finding.ToArray() + Unverifiable = $unverifiable.ToArray() + ExaminedCount = $examined + } +} + +Export-ModuleMember -Function @( + 'ConvertTo-ReferenceVersionMap', + 'Find-StaleBindingRedirect' +) diff --git a/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 b/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 new file mode 100644 index 000000000..8a20728b4 --- /dev/null +++ b/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 @@ -0,0 +1,335 @@ +Set-StrictMode -Version Latest + +BeforeAll { + $script:RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '../../..')).Path + Import-Module (Join-Path $script:RepoRoot 'scripts/dependencies/PackageGraph.psm1') -Force + Import-Module (Join-Path $script:RepoRoot 'scripts/dependencies/BindingRedirectVerification.psm1') -Force + + # Every fixture below is an in-memory string. The repository-level tests read tracked + # files through ReadAllText on paths derived from the repository root and write nothing. + function ConvertTo-CrLf { + param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Text) + return ($Text -replace "`r?`n", "`r`n") + } + + function ConvertTo-DependentAssemblyXml { + param( + [Parameter(Mandatory = $true)][string]$Name, + [string]$OldVersion = '', + [string]$NewVersion = '' + ) + $redirect = '' + if ($NewVersion -ne '') { + $redirect = ' ' -f $OldVersion, $NewVersion + } + $lines = @( + ' ', + (' ' -f $Name) + ) + if ($redirect -ne '') { $lines += $redirect } + $lines += ' ' + return ($lines -join "`r`n") + } + + function ConvertTo-AppConfigFixture { + param([Parameter(Mandatory = $true)][string[]]$Block) + $head = @( + '', + '', + ' ', + ' ' + ) -join "`r`n" + $tail = @( + ' ', + ' ', + '' + ) -join "`r`n" + return ($head + "`r`n" + ($Block -join "`r`n") + "`r`n" + $tail + "`r`n") + } + + $script:BlockFizzlerStale = ConvertTo-DependentAssemblyXml -Name 'Fizzler' -OldVersion '0.0.0.0-1.3.0.0' -NewVersion '1.3.0.0' + $script:BlockFizzlerCurrent = ConvertTo-DependentAssemblyXml -Name 'Fizzler' -OldVersion '0.0.0.0-1.3.1.0' -NewVersion '1.3.1.0' + $script:BlockFizzlerWideRange = ConvertTo-DependentAssemblyXml -Name 'Fizzler' -OldVersion '0.0.0.0-9.9.9.9' -NewVersion '1.3.1.0' + $script:BlockUnsafe = ConvertTo-DependentAssemblyXml -Name 'System.Runtime.CompilerServices.Unsafe' -OldVersion '0.0.0.0-6.0.3.0' -NewVersion '6.0.3.0' + $script:BlockUnknown = ConvertTo-DependentAssemblyXml -Name 'Contoso.Unknown' -OldVersion '0.0.0.0-1.0.0.0' -NewVersion '1.0.0.0' + $script:BlockNoRedirect = ConvertTo-DependentAssemblyXml -Name 'Contoso.NoRedirect' + + # Provider that deploys one version per known assembly and nothing for any other name. + $script:ProviderSingle = { + param($Name) + switch ($Name) { + 'Fizzler' { @('1.3.1.0') } + 'System.Runtime.CompilerServices.Unsafe' { @('6.0.3.0') } + default { @() } + } + } + + # Provider that deploys two versions of Fizzler. + $script:ProviderTwoVersions = { + param($Name) + switch ($Name) { + 'Fizzler' { @('1.3.0.0', '1.3.1.0') } + default { @() } + } + } + + $script:ProjectA = ConvertTo-CrLf -Text (@' + + + + ..\packages\Fizzler.1.3.1\lib\net47\Fizzler.dll + + + + + +'@) + + $script:ProjectB = ConvertTo-CrLf -Text (@' + + + + ..\packages\Fizzler.1.3.0\lib\net47\Fizzler.dll + + + + +'@) +} + +Describe 'Find-StaleBindingRedirect (in-memory fixtures)' { + + It 'reports one finding when the Fizzler redirect names 1.3.0.0 and the provider deploys 1.3.1.0' { + # Arrange: negative control, a stale Fizzler redirect beside a correct Unsafe redirect. + $text = ConvertTo-AppConfigFixture -Block @($script:BlockFizzlerStale, $script:BlockUnsafe) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderSingle + + # Assert + $result.Finding.Count | Should -Be 1 + $result.Finding[0].AssemblyName | Should -BeExactly 'Fizzler' + $result.Finding[0].NewVersion | Should -BeExactly '1.3.0.0' + $result.Finding[0].DeployedVersions.Count | Should -Be 1 + $result.Finding[0].DeployedVersions[0] | Should -BeExactly '1.3.1.0' + $result.ExaminedCount | Should -Be 2 + } + + It 'reports no finding when the Fizzler redirect names the deployed 1.3.1.0' { + # Arrange: positive control, the same shape with the correct Fizzler version. + $text = ConvertTo-AppConfigFixture -Block @($script:BlockFizzlerCurrent, $script:BlockUnsafe) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderSingle + + # Assert + $result.Finding.Count | Should -Be 0 + $result.ExaminedCount | Should -Be 2 + $result.Unverifiable.Count | Should -Be 0 + } + + It 'compares newVersion only and ignores the oldVersion range' { + # Arrange: a wide oldVersion range with a newVersion equal to the deployed version. + $text = ConvertTo-AppConfigFixture -Block @($script:BlockFizzlerWideRange) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderSingle + + # Assert + $result.Finding.Count | Should -Be 0 + } + + It 'lists an assembly the provider knows nothing about as unverifiable and not as a finding' { + # Arrange + $text = ConvertTo-AppConfigFixture -Block @($script:BlockUnknown) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderSingle + + # Assert + $result.Unverifiable.Count | Should -Be 1 + $result.Unverifiable[0] | Should -BeExactly 'Contoso.Unknown' + $result.Finding.Count | Should -Be 0 + $result.ExaminedCount | Should -Be 1 + } + + It 'skips a dependentAssembly block that carries no bindingRedirect' { + # Arrange: one block with no redirect beside one block with a correct redirect. + $text = ConvertTo-AppConfigFixture -Block @($script:BlockNoRedirect, $script:BlockFizzlerCurrent) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderSingle + + # Assert + $result.ExaminedCount | Should -Be 1 + $result.Finding.Count | Should -Be 0 + $result.Unverifiable.Count | Should -Be 0 + } + + It 'counts one examined entry per bindingRedirect-bearing block' { + # Arrange + $text = ConvertTo-AppConfigFixture -Block @($script:BlockFizzlerCurrent, $script:BlockUnsafe, $script:BlockUnknown) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderSingle + + # Assert + $result.ExaminedCount | Should -Be 3 + } + + It 'examines zero entries and reports nothing for empty text' { + # Arrange + $act = { Find-StaleBindingRedirect -AppConfigText '' -DeployedVersionProvider $script:ProviderSingle } + + # Act + $result = & $act + + # Assert + $act | Should -Not -Throw + $result.ExaminedCount | Should -Be 0 + $result.Finding.Count | Should -Be 0 + $result.Unverifiable.Count | Should -Be 0 + } + + It 'accepts a newVersion equal to any one of several deployed versions' { + # Arrange: the provider deploys both 1.3.0.0 and 1.3.1.0. + $text = ConvertTo-AppConfigFixture -Block @($script:BlockFizzlerStale) + + # Act + $result = Find-StaleBindingRedirect -AppConfigText $text -DeployedVersionProvider $script:ProviderTwoVersions + + # Assert + $result.Finding.Count | Should -Be 0 + } + + It 'throws when the text is not an application configuration document' { + # Arrange + $act = { Find-StaleBindingRedirect -AppConfigText '' -DeployedVersionProvider $script:ProviderSingle } + + # Act and Assert: the parser rejects text with no configuration root. + $act | Should -Throw + } +} + +Describe 'ConvertTo-ReferenceVersionMap (in-memory fixtures)' { + + It 'maps a Reference Include with a Version to its assembly name' { + # Arrange and Act + $map = ConvertTo-ReferenceVersionMap -ProjectText @($script:ProjectA) + + # Assert + $map.ContainsKey('Fizzler') | Should -BeTrue + $map['Fizzler'].Count | Should -Be 1 + $map['Fizzler'][0] | Should -BeExactly '1.3.1.0' + } + + It 'omits a Reference Include that declares no Version' { + # Arrange and Act + $map = ConvertTo-ReferenceVersionMap -ProjectText @($script:ProjectA) + + # Assert + $map.ContainsKey('System.Xml') | Should -BeFalse + $map.Keys.Count | Should -Be 1 + } + + It 'unions the versions of one assembly across several project texts' { + # Arrange and Act + $map = ConvertTo-ReferenceVersionMap -ProjectText @($script:ProjectA, $script:ProjectB) + + # Assert + (@($map['Fizzler'] | Sort-Object) -join ',') | Should -BeExactly '1.3.0.0,1.3.1.0' + } +} + +Describe 'Repository binding redirects (issue 953)' { + + It 'names 1.3.1.0 in every Fizzler binding redirect across the repository app.config files' { + # Arrange: every app.config directly under a root-level directory. + $configPath = @( + Get-ChildItem -LiteralPath $script:RepoRoot -Directory | + ForEach-Object { Join-Path $_.FullName 'app.config' } | + Where-Object { Test-Path -LiteralPath $_ } + ) + $configPath.Count | Should -BeGreaterThan 9 + + # Act + $fizzler = @( + foreach ($path in $configPath) { + $text = [System.IO.File]::ReadAllText($path) + foreach ($record in @(ConvertFrom-AppConfigText -Text $text | Where-Object { $_.Name -eq 'Fizzler' })) { + [pscustomobject]@{ + Config = Split-Path -Leaf (Split-Path -Parent $path) + NewVersion = $record.NewVersion + OldVersion = $record.OldVersion + } + } + } + ) + $stale = @($fizzler | Where-Object { $_.NewVersion -ne '1.3.1.0' -or $_.OldVersion -ne '0.0.0.0-1.3.1.0' }) + + # Assert + $fizzler.Count | Should -Be 13 -Because 'thirteen configs carry a Fizzler redirect' + $stale.Count | Should -Be 0 -Because ('these configs redirect Fizzler to another version: ' + (($stale | ForEach-Object { $_.Config + '=' + $_.NewVersion }) -join '; ')) + } + + It 'reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference' { + # Arrange: every app.config and every csproj directly under a root-level directory. + $rootDirectory = @(Get-ChildItem -LiteralPath $script:RepoRoot -Directory) + $configPath = @( + $rootDirectory | + ForEach-Object { Join-Path $_.FullName 'app.config' } | + Where-Object { Test-Path -LiteralPath $_ } + ) + $projectPath = @( + $rootDirectory | + ForEach-Object { Get-ChildItem -LiteralPath $_.FullName -Filter '*.csproj' -File } | + ForEach-Object { $_.FullName } + ) + $configPath.Count | Should -BeGreaterThan 9 + $projectPath.Count | Should -BeGreaterThan 9 + + $map = ConvertTo-ReferenceVersionMap -ProjectText @($projectPath | ForEach-Object { [System.IO.File]::ReadAllText($_) }) + $provider = { param($Name) $map[$Name] }.GetNewClosure() + $expectedDebt = @( + 'Azure.Core|1.62.0.0', + 'Microsoft.Bcl.Memory|10.0.0.7', + 'Microsoft.Bcl.Numerics|10.0.0.5', + 'Microsoft.Extensions.Diagnostics.Abstractions|10.0.0.5', + 'Microsoft.Identity.Client|4.89.0.0', + 'Microsoft.Identity.Client.Extensions.Msal|4.89.0.0', + 'Microsoft.IdentityModel.Abstractions|8.22.0.0', + 'Microsoft.IdentityModel.JsonWebTokens|8.22.0.0', + 'Microsoft.IdentityModel.Logging|8.22.0.0', + 'Microsoft.IdentityModel.Protocols|8.22.0.0', + 'Microsoft.IdentityModel.Protocols.OpenIdConnect|8.22.0.0', + 'Microsoft.IdentityModel.Tokens|8.22.0.0', + 'Microsoft.IdentityModel.Validators|8.22.0.0', + 'System.IdentityModel.Tokens.Jwt|8.22.0.0', + 'System.ClientModel|1.3.0.0' + ) + $expectedUnverifiable = @('Microsoft.IdentityModel.Clients.ActiveDirectory', 'System.Linq.AsyncEnumerable', 'netstandard') + + # Act + $redirectElement = 0 + $examined = 0 + $pair = [System.Collections.Generic.List[string]]::new() + $name = [System.Collections.Generic.List[string]]::new() + foreach ($path in $configPath) { + $text = [System.IO.File]::ReadAllText($path) + $redirectElement += [regex]::Matches($text, ' Date: Fri, 2 Oct 2026 03:38:39 -0400 Subject: [PATCH 12/14] test(953): complete final QC loop, footprint audit and AC check-off for the redirect gate Co-Authored-By: Claude Sonnet 5.5 --- ...16-known-debt-followup.2026-10-02T03-54.md | 35 ++++++++ ...-reduced-audit-handoff.2026-10-02T03-56.md | 81 +++++++++++++++++ ...t1-poshqc-format.iter1.2026-10-02T03-29.md | 65 ++++++++++++++ ...t1-poshqc-format.iter2.2026-10-02T03-33.md | 40 +++++++++ .../p2-t10-ac2-checkoff.2026-10-02T03-48.md | 14 +++ .../p2-t11-ac3-checkoff.2026-10-02T03-49.md | 15 ++++ .../p2-t12-ac4-checkoff.2026-10-02T03-50.md | 15 ++++ .../p2-t13-ac5-checkoff.2026-10-02T03-51.md | 14 +++ .../p2-t14-ac6-checkoff.2026-10-02T03-52.md | 18 ++++ .../p2-t15-ac-status.2026-10-02T03-53.md | 18 ++++ ...2-poshqc-analyze.iter2.2026-10-02T03-35.md | 23 +++++ ...2-t3-poshqc-test.iter2.2026-10-02T03-38.md | 41 +++++++++ .../p2-t4-loop-closure.2026-10-02T03-40.md | 26 ++++++ ...2-t5-function-test-map.2026-10-02T03-41.md | 41 +++++++++ .../p2-t6-file-size-audit.2026-10-02T03-42.md | 20 +++++ .../p2-t7-no-csharp-scope.2026-10-02T03-43.md | 18 ++++ .../p2-t8-footprint.2026-10-02T03-44.md | 89 +++++++++++++++++++ .../p2-t9-ac1-checkoff.2026-10-02T03-47.md | 16 ++++ .../issue.md | 12 +-- .../plan.2026-10-02T00-16.md | 34 +++---- ...irect-remedy-and-redirect-gate-research.md | 2 +- .../BindingRedirectVerification.psm1 | 2 +- .../BindingRedirectVerification.Tests.ps1 | 6 +- 23 files changed, 617 insertions(+), 28 deletions(-) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t16-known-debt-followup.2026-10-02T03-54.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t17-reduced-audit-handoff.2026-10-02T03-56.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter1.2026-10-02T03-29.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter2.2026-10-02T03-33.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t10-ac2-checkoff.2026-10-02T03-48.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t11-ac3-checkoff.2026-10-02T03-49.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t12-ac4-checkoff.2026-10-02T03-50.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t13-ac5-checkoff.2026-10-02T03-51.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t14-ac6-checkoff.2026-10-02T03-52.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t15-ac-status.2026-10-02T03-53.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t2-poshqc-analyze.iter2.2026-10-02T03-35.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t3-poshqc-test.iter2.2026-10-02T03-38.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t4-loop-closure.2026-10-02T03-40.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t5-function-test-map.2026-10-02T03-41.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t6-file-size-audit.2026-10-02T03-42.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t7-no-csharp-scope.2026-10-02T03-43.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t8-footprint.2026-10-02T03-44.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t9-ac1-checkoff.2026-10-02T03-47.md diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t16-known-debt-followup.2026-10-02T03-54.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t16-known-debt-followup.2026-10-02T03-54.md new file mode 100644 index 000000000..6047a4979 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t16-known-debt-followup.2026-10-02T03-54.md @@ -0,0 +1,35 @@ +# P2-T16 Follow-up note for the coordinator: known-debt binding redirects + +Timestamp: 2026-10-02T03-54 +Command: none (documentation artifact derived from plan section 7, re-measured at P0-T8 and pinned by test 14 at P1-T15 and P2-T3) +EXIT_CODE: 0 + +FOLLOW-UP: known-debt redirect correction, 15 pairs, 137 entries; promotion by the coordinator + +Known-debt pairs (assembly | config newVersion | only csproj Reference version | configs carrying it): + +1. Azure.Core | 1.62.0.0 | 1.63.0.0 | 6 +2. Microsoft.Bcl.Memory | 10.0.0.7 | 10.0.0.12 | 10 +3. Microsoft.Bcl.Numerics | 10.0.0.5 | 10.0.0.12 | 12 +4. Microsoft.Extensions.Diagnostics.Abstractions | 10.0.0.5 | 10.0.0.12 | 6 +5. Microsoft.Identity.Client | 4.89.0.0 | 4.90.1.0 | 6 +6. Microsoft.Identity.Client.Extensions.Msal | 4.89.0.0 | 4.90.1.0 | 6 +7. Microsoft.IdentityModel.Abstractions | 8.22.0.0 | 8.23.0.0 | 6 +8. Microsoft.IdentityModel.JsonWebTokens | 8.22.0.0 | 8.23.0.0 | 7 +9. Microsoft.IdentityModel.Logging | 8.22.0.0 | 8.23.0.0 | 7 +10. Microsoft.IdentityModel.Protocols | 8.22.0.0 | 8.23.0.0 | 13 +11. Microsoft.IdentityModel.Protocols.OpenIdConnect | 8.22.0.0 | 8.23.0.0 | 13 +12. Microsoft.IdentityModel.Tokens | 8.22.0.0 | 8.23.0.0 | 13 +13. Microsoft.IdentityModel.Validators | 8.22.0.0 | 8.23.0.0 | 13 +14. System.IdentityModel.Tokens.Jwt | 8.22.0.0 | 8.23.0.0 | 13 +15. System.ClientModel | 1.3.0.0 | 1.16.0.0 | 6 + +Total: 137 redirect entries across the 15 pairs (6 + 10 + 12 + 6 + 6 + 6 + 6 + 7 + 7 + 13 + 13 + 13 + 13 + 13 + 6). + +Unverifiable names (no csproj `Reference Include="Name, Version=` anywhere): System.Linq.AsyncEnumerable (15 entries), Microsoft.IdentityModel.Clients.ActiveDirectory (13 entries), netstandard (1 entry); 29 entries in all. + +Scope statement: correcting these 15 pairs is out of scope for issue 953. The repository-level ratchet (test 14 in `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`) pins exactly this set, so correcting a pair makes test 14 fail on a stale known-debt entry until the recorded literal is updated in the same change. + +Creation statement: this plan created no issue and no potential entry. The coordinator promotes this follow-up through the potential-entry lifecycle. + +Output Summary: 15 known-debt pairs (137 entries) and 3 unverifiable names (29 entries) recorded for coordinator promotion; no issue or potential entry created. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t17-reduced-audit-handoff.2026-10-02T03-56.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t17-reduced-audit-handoff.2026-10-02T03-56.md new file mode 100644 index 000000000..7805fedb3 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/p2-t17-reduced-audit-handoff.2026-10-02T03-56.md @@ -0,0 +1,81 @@ +# P2-T17 Reduced-audit handoff index + +Timestamp: 2026-10-02T03-56 +Command: Glob `**/*.md` over `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence` +EXIT_CODE: 0 + +AUDIT-MODE: reduced (minor-audit) +COVERAGE-SOURCE: CI +COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies) + +D8 is this plan's exception to the atomic-plan-contract Coverage Evidence Contract: no numeric baseline or post-change coverage figure is recorded locally because no local Pester coverage route instruments `scripts/dependencies`; the figure is read from the CI Pester job (`_pester.yml`, floor 80 percent, repository rule 85 percent) and per-file targets from the `pester-coverage` JaCoCo artifact, by the orchestrator after the push. + +BASE_SHA: 860d67bf4fddecb929e0d6c166065fd1ee752feb + +Write Set (plan section 5): the 11 configs QuickFiler/app.config, QuickFiler.Test/app.config, SVGControl.Test/app.config, Tags/app.config, TaskMaster/app.config, TaskTree/app.config, TaskVisualization/app.config, TaskVisualization.Test/app.config, ToDoModel/app.config, ToDoModel.Test/app.config, UtilitiesCS.Test/app.config; `scripts/dependencies/BindingRedirectVerification.psm1` (new); `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` (new); and the feature folder (issue.md AC check-offs, this plan's checkboxes, `evidence/`). + +GATE-SUBSTITUTION lines used: `GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count` (P0-T11 and P2-T2; the lint result is recorded as `PoshQC analyze: pass (0 findings); tool reports no count`). The PoshQC MCP tools stood in for raw Invoke-Formatter, Invoke-ScriptAnalyzer and Invoke-Pester; Pester coverage is read from CI. + +Budget outcome: no denial. The new module consumed one production slot and the new test file one test slot; no PowerShell write was denied in Phase 2, and no state file or override variable was touched. + +Terminal loop iteration N: 2. Iteration 1 was non-terminal (the formatter rewrote the two new Write Set files, indentation only; REWRITE-COUNT 2). Iteration 2: REWRITE-COUNT 0, analyze `ok` true, test failures 0. + +Final suite counts (P2-T3 iteration 2): JUNIT-ROOT tests=151 failures=0 errors=0 disabled=0; 9 suites: AnalyzerItemRepair 13, BindingRedirectVerification 14, ConsistencyVerifier 14, DependabotConfig 17, PackageCompatibility 8, PackageGraph 32, ProjectConsistency 18, Repair-PackageManifestConsistency 31, RepositoryTreeConsistency 4; JUNIT-NOTPASSED: none. + +Acceptance criteria: AC1 to AC6 checked off in issue.md (6 of 6). AC6's CI coverage figure remains to be read from the CI Pester job. + +Artifacts written by P0-T1 through P2-T16 (paths relative to `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`; this index names itself only as the index): + +```text +evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md +evidence/baseline/p0-t2-base-anchor.2026-10-02T03-08.md +evidence/baseline/p0-t3-ac-precondition.2026-10-02T03-08.md +evidence/baseline/p0-t4-fizzler-census.2026-10-02T03-08.md +evidence/baseline/p0-t5-shared-string-census.2026-10-02T03-08.md +evidence/baseline/p0-t6-unsafe-census.2026-10-02T03-08.md +evidence/baseline/p0-t7-encoding-baseline.2026-10-02T03-08.md +evidence/baseline/p0-t8-known-debt-remeasure.2026-10-02T03-08.md +evidence/baseline/p0-t9-linecount-baseline.2026-10-02T03-08.md +evidence/baseline/p0-t10-poshqc-format.2026-10-02T03-08.md +evidence/baseline/p0-t11-poshqc-analyze.2026-10-02T03-08.md +evidence/baseline/p0-t12-poshqc-test.2026-10-02T03-08.md +evidence/baseline/p0-t13-budget-baseline.2026-10-02T03-08.md +evidence/qa-gates/p1-t1-module-authored.2026-10-02T03-18.md +evidence/regression-testing/p1-t2-tests-authored.2026-10-02T03-18.md +evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md +evidence/qa-gates/p1-t4-quickfiler-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t5-quickfiler-test-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t6-svgcontrol-test-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t7-tags-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t8-taskmaster-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t9-tasktree-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t10-taskvisualization-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t11-taskvisualization-test-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t12-todomodel-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t13-todomodel-test-redirect.2026-10-02T03-21.md +evidence/qa-gates/p1-t14-utilitiescs-test-redirect.2026-10-02T03-21.md +evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md +evidence/qa-gates/p1-t16-sweep-verification.2026-10-02T03-21.md +evidence/qa-gates/p1-t17-unsafe-unchanged.2026-10-02T03-21.md +evidence/qa-gates/p2-t1-poshqc-format.iter1.2026-10-02T03-29.md +evidence/qa-gates/p2-t1-poshqc-format.iter2.2026-10-02T03-33.md +evidence/qa-gates/p2-t2-poshqc-analyze.iter2.2026-10-02T03-35.md +evidence/qa-gates/p2-t3-poshqc-test.iter2.2026-10-02T03-38.md +evidence/qa-gates/p2-t4-loop-closure.2026-10-02T03-40.md +evidence/qa-gates/p2-t5-function-test-map.2026-10-02T03-41.md +evidence/qa-gates/p2-t6-file-size-audit.2026-10-02T03-42.md +evidence/qa-gates/p2-t7-no-csharp-scope.2026-10-02T03-43.md +evidence/qa-gates/p2-t8-footprint.2026-10-02T03-44.md +evidence/qa-gates/p2-t9-ac1-checkoff.2026-10-02T03-47.md +evidence/qa-gates/p2-t10-ac2-checkoff.2026-10-02T03-48.md +evidence/qa-gates/p2-t11-ac3-checkoff.2026-10-02T03-49.md +evidence/qa-gates/p2-t12-ac4-checkoff.2026-10-02T03-50.md +evidence/qa-gates/p2-t13-ac5-checkoff.2026-10-02T03-51.md +evidence/qa-gates/p2-t14-ac6-checkoff.2026-10-02T03-52.md +evidence/qa-gates/p2-t15-ac-status.2026-10-02T03-53.md +evidence/other/p2-t16-known-debt-followup.2026-10-02T03-54.md +``` + +Acceptance: every artifact path named by P0-T1 through P2-T16 exists (the Glob listed all 47 paths above) and is listed; the literals `AUDIT-MODE: reduced (minor-audit)` and `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)` are present. + +Output Summary: Index of 47 artifacts written by P0-T1 through P2-T16 (P2-T1 has two iteration artifacts), with BASE_SHA, Write Set, COVERAGE-SOURCE CI, GATE-SUBSTITUTION lines, no budget denial, terminal loop iteration 2 and 151 tests with 0 failures. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter1.2026-10-02T03-29.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter1.2026-10-02T03-29.md new file mode 100644 index 000000000..74359f84a --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter1.2026-10-02T03-29.md @@ -0,0 +1,65 @@ +# P2-T1 Final QC format step, iteration 1 (non-terminal) + +Timestamp: 2026-10-02T03-29 +Command: git -C hash-object <16 .ps1/.psm1 paths under scripts/dependencies and tests/scripts/dependencies> (CMD-HASHSET, before); MCP mcp__drm-copilot__run_poshqc_format with workspace_root `` and scan_folders `["scripts/dependencies","tests/scripts/dependencies"]`; CMD-HASHSET (after) +EXIT_CODE: 0 + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_format","workspace_root":"","summary":"Ran bundled PoshQC format against '' with 2 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): payload `ok` true, so 0. The 2-folder summary literal is present. + +Files enumerated with the Glob tool at run time: 7 under scripts/dependencies, 9 under tests/scripts/dependencies, 16 in all. + +HASH before (sorted by path): + +```text +HASH scripts/dependencies/AnalyzerItemRepair.psm1 97e07385e44d57462975875d03dc639a0cf37448 +HASH scripts/dependencies/BindingRedirectVerification.psm1 8c1539be3e316e887abd9381abe4e4a93c7f76bc +HASH scripts/dependencies/ConsistencyVerifier.psm1 a3d71fac2681531816fc4d9f6062e4a47fbf7a84 +HASH scripts/dependencies/PackageCompatibility.psm1 0a7a10463911d2e4d9bea73522a6b1bb4aa97c56 +HASH scripts/dependencies/PackageGraph.psm1 ca1579f7eb17066fe86578a6b5f04c92e3682a18 +HASH scripts/dependencies/ProjectConsistency.psm1 334c1b5bf74d0d5f688aae7532f7498246b0a6d4 +HASH scripts/dependencies/Repair-PackageManifestConsistency.ps1 6d13903d57eab1d0f751ff0452c989103500bf35 +HASH tests/scripts/dependencies/AnalyzerItemRepair.Tests.ps1 8c9be4625935462e5c1d59200dcaaf3978b0277b +HASH tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 8a20728b437b6ddc7f5e44e7f15c1184fbf80931 +HASH tests/scripts/dependencies/ConsistencyVerifier.Tests.ps1 26e5d3e8be66c729b2d67d423e93729d3ed0c06f +HASH tests/scripts/dependencies/DependabotConfig.Tests.ps1 b740c118927caf1ef1aed74250602b2ea43f09d7 +HASH tests/scripts/dependencies/PackageCompatibility.Tests.ps1 d5409dff1556ef0770f98b51db1c2f1d6abd9428 +HASH tests/scripts/dependencies/PackageGraph.Tests.ps1 2977c2c7527b120ab807e171b102366b924cbf89 +HASH tests/scripts/dependencies/ProjectConsistency.Tests.ps1 57dc48c5439f01b21bc0948fe9369451108ec0d9 +HASH tests/scripts/dependencies/Repair-PackageManifestConsistency.Tests.ps1 7c8d6b697a99ad9c48d79448806a6cf629dafb7f +HASH tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 4fbea957f91aa94fb90c03e829a2783208879a0b +``` + +HASH after (sorted by path); the two differing entries are marked: + +```text +HASH scripts/dependencies/AnalyzerItemRepair.psm1 97e07385e44d57462975875d03dc639a0cf37448 +HASH scripts/dependencies/BindingRedirectVerification.psm1 620bc82376f8d9b849b6728cec2c7aabbda60e5b (changed) +HASH scripts/dependencies/ConsistencyVerifier.psm1 a3d71fac2681531816fc4d9f6062e4a47fbf7a84 +HASH scripts/dependencies/PackageCompatibility.psm1 0a7a10463911d2e4d9bea73522a6b1bb4aa97c56 +HASH scripts/dependencies/PackageGraph.psm1 ca1579f7eb17066fe86578a6b5f04c92e3682a18 +HASH scripts/dependencies/ProjectConsistency.psm1 334c1b5bf74d0d5f688aae7532f7498246b0a6d4 +HASH scripts/dependencies/Repair-PackageManifestConsistency.ps1 6d13903d57eab1d0f751ff0452c989103500bf35 +HASH tests/scripts/dependencies/AnalyzerItemRepair.Tests.ps1 8c9be4625935462e5c1d59200dcaaf3978b0277b +HASH tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 5f67da131c8655530ba8a4e67998d0f153506bcf (changed) +HASH tests/scripts/dependencies/ConsistencyVerifier.Tests.ps1 26e5d3e8be66c729b2d67d423e93729d3ed0c06f +HASH tests/scripts/dependencies/DependabotConfig.Tests.ps1 b740c118927caf1ef1aed74250602b2ea43f09d7 +HASH tests/scripts/dependencies/PackageCompatibility.Tests.ps1 d5409dff1556ef0770f98b51db1c2f1d6abd9428 +HASH tests/scripts/dependencies/PackageGraph.Tests.ps1 2977c2c7527b120ab807e171b102366b924cbf89 +HASH tests/scripts/dependencies/ProjectConsistency.Tests.ps1 57dc48c5439f01b21bc0948fe9369451108ec0d9 +HASH tests/scripts/dependencies/Repair-PackageManifestConsistency.Tests.ps1 7c8d6b697a99ad9c48d79448806a6cf629dafb7f +HASH tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 4fbea957f91aa94fb90c03e829a2783208879a0b +``` + +REWRITE: scripts/dependencies/BindingRedirectVerification.psm1 +REWRITE: tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 +REWRITE-COUNT: 2 + +Both rewritten files are Write Set files (new files of this item, committed at HEAD 45e3f8a2e). `git diff -U0` against the working tree shows only continuation-line indentation changes: one line in the module (`ForEach-Object { [string]$_ })` indented 4 further spaces) and three lines in the test file (lines 251, 281 and 286: `Where-Object` / `ForEach-Object` continuation lines indented 4 further spaces). No file outside the Write Set was rewritten. This iteration is non-terminal; the loop restarts at P2-T1 as iteration 2 (P2-T2 and P2-T3 are not run on this iteration). + +Output Summary: PoshQC format ok true; 2 of 16 hash entries changed (the two new Write Set files, indentation-only rewrites); REWRITE-COUNT 2, so the iteration is non-terminal and the loop restarts as iteration 2. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter2.2026-10-02T03-33.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter2.2026-10-02T03-33.md new file mode 100644 index 000000000..c0e544a29 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter2.2026-10-02T03-33.md @@ -0,0 +1,40 @@ +# P2-T1 Final QC format step, iteration 2 (terminal) + +Timestamp: 2026-10-02T03-33 +Command: MCP mcp__drm-copilot__run_poshqc_format with workspace_root `` and scan_folders `["scripts/dependencies","tests/scripts/dependencies"]`, bracketed by git -C hash-object <16 .ps1/.psm1 paths under scripts/dependencies and tests/scripts/dependencies> (CMD-HASHSET before and after) +EXIT_CODE: 0 + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_format","workspace_root":"","summary":"Ran bundled PoshQC format against '' with 2 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): payload `ok` true, so 0. The 2-folder summary literal is present. + +Sequence observed in this iteration: the formatter was called once and the hash set was taken after it (identical to the iteration 1 after-set); the formatter was then called a second time and the hash set taken again. The set taken before the second call is the CMD-HASHSET before-set of this record and the set taken after the second call is the after-set. No PowerShell file was written between the two sets (the only intervening write was this iteration's predecessor Markdown artifact). The two sets are identical, 16 entries each, both new files present: + +```text +HASH scripts/dependencies/AnalyzerItemRepair.psm1 97e07385e44d57462975875d03dc639a0cf37448 +HASH scripts/dependencies/BindingRedirectVerification.psm1 620bc82376f8d9b849b6728cec2c7aabbda60e5b +HASH scripts/dependencies/ConsistencyVerifier.psm1 a3d71fac2681531816fc4d9f6062e4a47fbf7a84 +HASH scripts/dependencies/PackageCompatibility.psm1 0a7a10463911d2e4d9bea73522a6b1bb4aa97c56 +HASH scripts/dependencies/PackageGraph.psm1 ca1579f7eb17066fe86578a6b5f04c92e3682a18 +HASH scripts/dependencies/ProjectConsistency.psm1 334c1b5bf74d0d5f688aae7532f7498246b0a6d4 +HASH scripts/dependencies/Repair-PackageManifestConsistency.ps1 6d13903d57eab1d0f751ff0452c989103500bf35 +HASH tests/scripts/dependencies/AnalyzerItemRepair.Tests.ps1 8c9be4625935462e5c1d59200dcaaf3978b0277b +HASH tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 5f67da131c8655530ba8a4e67998d0f153506bcf +HASH tests/scripts/dependencies/ConsistencyVerifier.Tests.ps1 26e5d3e8be66c729b2d67d423e93729d3ed0c06f +HASH tests/scripts/dependencies/DependabotConfig.Tests.ps1 b740c118927caf1ef1aed74250602b2ea43f09d7 +HASH tests/scripts/dependencies/PackageCompatibility.Tests.ps1 d5409dff1556ef0770f98b51db1c2f1d6abd9428 +HASH tests/scripts/dependencies/PackageGraph.Tests.ps1 2977c2c7527b120ab807e171b102366b924cbf89 +HASH tests/scripts/dependencies/ProjectConsistency.Tests.ps1 57dc48c5439f01b21bc0948fe9369451108ec0d9 +HASH tests/scripts/dependencies/Repair-PackageManifestConsistency.Tests.ps1 7c8d6b697a99ad9c48d79448806a6cf629dafb7f +HASH tests/scripts/dependencies/RepositoryTreeConsistency.Tests.ps1 4fbea957f91aa94fb90c03e829a2783208879a0b +``` + +REWRITE-COUNT: 0 + +Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; before and after hash sets identical (16 entries, both new files present); `REWRITE-COUNT: 0` recorded. + +Output Summary: PoshQC format ok true; hash sets identical across the call (16 entries); REWRITE-COUNT 0. Iteration 2 follows the iteration 1 indentation rewrite of the two new files. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t10-ac2-checkoff.2026-10-02T03-48.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t10-ac2-checkoff.2026-10-02T03-48.md new file mode 100644 index 000000000..29c1831b6 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t10-ac2-checkoff.2026-10-02T03-48.md @@ -0,0 +1,14 @@ +# P2-T10 Check off AC2 + +Timestamp: 2026-10-02T03-48 +Command: Edit tool on `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (`- [ ] AC2:` to `- [x] AC2:`); Grep pattern `^- \[x\] AC2:` count mode over issue.md +EXIT_CODE: 0 + +Checked off AC: AC2 in issue.md (line 85), changing only `- [ ]` to `- [x]`; the criterion text is unchanged. Grep count of `^- \[x\] AC2:` is 1. + +Evidence cited: + +- P0-T6: baseline census, 17 `System.Runtime.CompilerServices.Unsafe` blocks, every one `oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0"`. +- P1-T17: after the edits, the same 17 blocks unchanged, and the BASE_SHA-anchored `app.config` diff carries no content line containing `Unsafe` (22 content lines, all Fizzler redirect lines). + +Output Summary: AC2 checked off in issue.md; Grep count of the checked line is 1; text unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t11-ac3-checkoff.2026-10-02T03-49.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t11-ac3-checkoff.2026-10-02T03-49.md new file mode 100644 index 000000000..008603f90 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t11-ac3-checkoff.2026-10-02T03-49.md @@ -0,0 +1,15 @@ +# P2-T11 Check off AC3 + +Timestamp: 2026-10-02T03-49 +Command: Edit tool on `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (`- [ ] AC3:` to `- [x] AC3:`); Grep pattern `^- \[x\] AC3:` count mode over issue.md +EXIT_CODE: 0 + +Checked off AC: AC3 in issue.md (line 86), changing only `- [ ]` to `- [x]`; the criterion text is unchanged. Grep count of `^- \[x\] AC3:` is 1. + +Evidence cited: + +- P1-T1: `scripts/dependencies/BindingRedirectVerification.psm1` authored (`Find-StaleBindingRedirect`, `ConvertTo-ReferenceVersionMap`). +- P1-T2: `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` authored (14 It blocks). +- P2-T3 terminal pass (iteration 2: 9 suites, 151 tests, 0 failures; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`), which includes test 1 (negative control: one finding for the Fizzler 1.3.0.0 redirect against a provider deploying 1.3.1.0), test 2 (positive control: no finding, examined count 2) and test 6 (examined-entry count: three redirect-bearing blocks give 3). + +Output Summary: AC3 checked off in issue.md; Grep count of the checked line is 1; text unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t12-ac4-checkoff.2026-10-02T03-50.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t12-ac4-checkoff.2026-10-02T03-50.md new file mode 100644 index 000000000..24f5b80e0 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t12-ac4-checkoff.2026-10-02T03-50.md @@ -0,0 +1,15 @@ +# P2-T12 Check off AC4 + +Timestamp: 2026-10-02T03-50 +Command: Edit tool on `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (`- [ ] AC4:` to `- [x] AC4:`); Grep pattern `^- \[x\] AC4:` count mode over issue.md +EXIT_CODE: 0 + +Checked off AC: AC4 in issue.md (line 87), changing only `- [ ]` to `- [x]`; the criterion text is unchanged. Grep count of `^- \[x\] AC4:` is 1. + +Evidence cited: + +- P0-T8: baseline re-measurement of the known-debt table (every figure equal to section 7, no KNOWN-DEBT-DRIFT). +- P1-T15: pass-after run, 151 tests and 0 failures; test 14 passed over the 17 app.config and 18 csproj files. +- P2-T3 terminal pass (iteration 2: 151 tests, 0 failures) of test 14, `reports exactly the recorded known-debt set and unverifiable set over every app.config against every csproj Reference`: the finding set equals the 15 recorded pairs, none is Fizzler or Unsafe, and the unverifiable set equals the 3 recorded names (Microsoft.IdentityModel.Clients.ActiveDirectory, System.Linq.AsyncEnumerable, netstandard). + +Output Summary: AC4 checked off in issue.md; Grep count of the checked line is 1; text unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t13-ac5-checkoff.2026-10-02T03-51.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t13-ac5-checkoff.2026-10-02T03-51.md new file mode 100644 index 000000000..b65dbd548 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t13-ac5-checkoff.2026-10-02T03-51.md @@ -0,0 +1,14 @@ +# P2-T13 Check off AC5 + +Timestamp: 2026-10-02T03-51 +Command: Edit tool on `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (`- [ ] AC5:` to `- [x] AC5:`); Grep pattern `^- \[x\] AC5:` count mode over issue.md +EXIT_CODE: 0 + +Checked off AC: AC5 in issue.md (line 88), changing only `- [ ]` to `- [x]`; the criterion text is unchanged. Grep count of `^- \[x\] AC5:` is 1. + +Evidence cited: + +- P1-T3 (fail-before, `evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md`): test 13 failed before the 11 config edits with a message containing `but got 11`, naming the eleven stale directories. +- P1-T15 (pass-after, `evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md`): the same test passed after the edits (151 tests, 0 failures). + +Output Summary: AC5 checked off in issue.md; Grep count of the checked line is 1; text unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t14-ac6-checkoff.2026-10-02T03-52.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t14-ac6-checkoff.2026-10-02T03-52.md new file mode 100644 index 000000000..a8f763011 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t14-ac6-checkoff.2026-10-02T03-52.md @@ -0,0 +1,18 @@ +# P2-T14 Check off AC6 + +Timestamp: 2026-10-02T03-52 +Command: Edit tool on `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (`- [ ] AC6:` to `- [x] AC6:`); Grep pattern `^- \[x\] AC6:` count mode over issue.md +EXIT_CODE: 0 + +Checked off AC: AC6 in issue.md (line 89), changing only `- [ ]` to `- [x]`; the criterion text is unchanged. Grep count of `^- \[x\] AC6:` is 1. + +Evidence cited: + +- P2-T1 (format, terminal iteration 2: `ok` true, REWRITE-COUNT 0), P2-T2 (analyze, iteration 2: `ok` true), P2-T3 (test, iteration 2: 151 tests, 0 failures), P2-T4 (loop closure, terminal iteration N=2) and P2-T5 (per-function map: both exported functions have positive, negative and edge It names). +- Coverage deferral authority (D8): the AC6 text itself states that the Pester line-coverage figure for the new module is produced by the CI Pester job and `is not measured locally`. That literal is the authority for not recording a local coverage figure here. + +COVERAGE-SOURCE: CI + +The CI Pester coverage figure for `scripts/dependencies` (workflow floor 80 percent, repository rule 85 percent) is read by the orchestrator from the CI Pester job after the push; it is not recorded in this artifact. + +Output Summary: AC6 checked off in issue.md; Grep count of the checked line is 1; text unchanged. Coverage figure deferred to CI under D8 and the AC6 literal `is not measured locally`. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t15-ac-status.2026-10-02T03-53.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t15-ac-status.2026-10-02T03-53.md new file mode 100644 index 000000000..82ea554c0 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t15-ac-status.2026-10-02T03-53.md @@ -0,0 +1,18 @@ +# P2-T15 Acceptance-criteria status summary and plan reconciliation + +Timestamp: 2026-10-02T03-53 +Command: Grep pattern `^- \[x\] AC[1-6]:` count mode over issue.md; Grep pattern `^- \[ \] AC[1-6]:` count mode over issue.md +EXIT_CODE: 0 + +Observations: Grep count of `^- \[x\] AC[1-6]:` is 6; Grep count of `^- \[ \] AC[1-6]:` is 0 (no match). + +### Acceptance Criteria Status +- Source: docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md (`## Acceptance Criteria`, minor-audit) +- Total AC items: 6 +- Checked off (delivered): 6 +- Remaining (unchecked): 0 +- Items remaining: none + +Note: AC6 is checked off on the local PoshQC format, analyze and test evidence and the per-function test map; its Pester line-coverage figure is read from the CI Pester job after the push (COVERAGE-SOURCE: CI, D8), as AC6's own text states. + +Output Summary: 6 of 6 acceptance criteria checked off in issue.md; 0 unchecked. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t2-poshqc-analyze.iter2.2026-10-02T03-35.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t2-poshqc-analyze.iter2.2026-10-02T03-35.md new file mode 100644 index 000000000..ef74a91ba --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t2-poshqc-analyze.iter2.2026-10-02T03-35.md @@ -0,0 +1,23 @@ +# P2-T2 Final QC analyze step, iteration 2 (terminal) + +Timestamp: 2026-10-02T03-35 +Command: MCP mcp__drm-copilot__run_poshqc_analyze with workspace_root `` and scan_folders `["scripts/dependencies","tests/scripts/dependencies"]` +EXIT_CODE: 0 + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_analyze","workspace_root":"","summary":"Ran bundled PoshQC analyze against '' with 2 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): payload `ok` true, so 0. The 2-folder summary literal is present. + +GATE-SUBSTITUTION: PoshQC analyze ok flag stands in for a diagnostic count + +PoshQC analyze: pass (0 findings); tool reports no count. + +Type checking: not applicable to PowerShell (`.claude/rules/powershell.md` line 17); no type-check step was run. + +Iteration note: iteration 1 stopped at P2-T1 (formatter rewrote the two new files), so no P2-T2 artifact exists for iteration 1; this analyze run follows the iteration 2 format pass with REWRITE-COUNT 0. + +Output Summary: PoshQC analyze ok true over scripts/dependencies and tests/scripts/dependencies; the tool returns no diagnostic count, so the ok flag is the lint result. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t3-poshqc-test.iter2.2026-10-02T03-38.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t3-poshqc-test.iter2.2026-10-02T03-38.md new file mode 100644 index 000000000..cf444e75a --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t3-poshqc-test.iter2.2026-10-02T03-38.md @@ -0,0 +1,41 @@ +# P2-T3 Final QC test step, iteration 2 (terminal) + +Timestamp: 2026-10-02T03-38 +Command: pwsh -NoProfile -Command 'Remove-Item -LiteralPath /artifacts/pester/pester-junit.xml -ErrorAction SilentlyContinue' (CMD-JUNIT-DELETE); MCP mcp__drm-copilot__run_poshqc_test with workspace_root `` and scan_folders `["tests/scripts/dependencies"]`; CMD-JUNIT-READ Greps (`/artifacts/pester/pester-junit.xml` +EXIT_CODE: 0 + +CMD-JUNIT-DELETE: the pwsh invocation printed nothing and reported no error (exit code not printed by the Bash tool, which reported completion with no output). GLOB-BLIND: the Glob tool returned `No files found` both after the delete and after the test run, because it does not return this gitignored file (recorded at P0-T12); the Greps read the document by absolute path. The JUnit root and testsuite elements carry no `timestamp` attribute, so freshness is shown by the run sequence: delete, then run, then read; the root `time` is 5.685 against 5.460 at P1-T15, which a document surviving the delete could not report. + +Payload (verbatim, worktree root replaced per C4): + +```text +{"ok":true,"tool":"run_poshqc_test","workspace_root":"","summary":"Ran bundled PoshQC test against '' with 1 selected scan folder(s)."} +``` + +EXIT_CODE derivation (C3): payload `ok` true and JUNIT root `failures=0`, so 0. + +CMD-JUNIT-READ: + +```text +JUNIT-ROOT tests=151 failures=0 errors=0 disabled=0 +JUNIT-SUITE AnalyzerItemRepair.Tests.ps1 tests=13 failures=0 skipped=0 +JUNIT-SUITE BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0 +JUNIT-SUITE ConsistencyVerifier.Tests.ps1 tests=14 failures=0 skipped=0 +JUNIT-SUITE DependabotConfig.Tests.ps1 tests=17 failures=0 skipped=0 +JUNIT-SUITE PackageCompatibility.Tests.ps1 tests=8 failures=0 skipped=0 +JUNIT-SUITE PackageGraph.Tests.ps1 tests=32 failures=0 skipped=0 +JUNIT-SUITE ProjectConsistency.Tests.ps1 tests=18 failures=0 skipped=0 +JUNIT-SUITE Repair-PackageManifestConsistency.Tests.ps1 tests=31 failures=0 skipped=0 +JUNIT-SUITE RepositoryTreeConsistency.Tests.ps1 tests=4 failures=0 skipped=0 +JUNIT-NOTPASSED: none +``` + +The ` diff --name-only 860d67bf4fddecb929e0d6c166065fd1ee752feb -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'; git -C status --porcelain -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config' +EXIT_CODE: 0 + +Observations: + +- BASE_SHA-anchored diff over the six C# pathspecs: printed nothing. The BASE_SHA-anchored diff is the discriminating observation because Phases 0 and 1 are committed and pushed, so porcelain is empty for committed paths. +- Porcelain over the same six pathspecs: printed nothing. +- Pathspec discrimination control: the same BASE_SHA-anchored diff with the pathspec `'*/app.config'` printed the 11 Write Set config paths (QuickFiler.Test, QuickFiler, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization.Test, TaskVisualization, ToDoModel.Test, ToDoModel, UtilitiesCS.Test), so the wildcard pathspec form reaches nested project directories and the empty result is not a pathspec artifact. +- An earlier unquoted form of the same two commands (shell-expanded pathspecs) also printed nothing; the quoted form above is the one recorded. + +CSHARP-TOOLCHAIN: not applicable; no C# source, project, solution or manifest file changed (D11) + +Acceptance: both captures empty; the scope statement is present. + +Output Summary: No *.cs, *.csproj, *.sln, *.props, *.targets or packages.config path differs from BASE_SHA or appears in porcelain; the app.config pathspec control returned the 11 expected paths. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t8-footprint.2026-10-02T03-44.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t8-footprint.2026-10-02T03-44.md new file mode 100644 index 000000000..464ea95a7 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t8-footprint.2026-10-02T03-44.md @@ -0,0 +1,89 @@ +# P2-T8 Footprint assertion (CMD-FOOTPRINT) + +Timestamp: 2026-10-02T03-44 +Command: git -C diff --name-only 860d67bf4fddecb929e0d6c166065fd1ee752feb -- . ; git -C status --porcelain --untracked-files=all +EXIT_CODE: 0 + +Capture 1, `git diff --name-only -- .` (verbatim; the three git advisory lines about LF to CRLF on the plan and the two new files are omitted here and are not errors). Because Phases 0 and 1 are committed, this BASE_SHA-anchored diff is the discriminating observation and lists the whole item: + +```text +.claude/agent-memory/atomic-planner/MEMORY.md +.claude/agent-memory/atomic-planner/project_953_fizzler_redirect_sweep_and_ratchet_plan_seams.md +.claude/agent-memory/atomic-planner/project_953_r1_pwsh_refused_and_count_recheck_seams.md +.claude/agent-memory/atomic-planner/project_953_r2_grep_long_line_omission_and_cr_anchor_seams.md +.claude/agent-memory/atomic-planner/project_953_r3_grep_gitignore_directory_path_and_measured_line_lengths.md +QuickFiler.Test/app.config +QuickFiler/app.config +SVGControl.Test/app.config +Tags/app.config +TaskMaster/app.config +TaskTree/app.config +TaskVisualization.Test/app.config +TaskVisualization/app.config +ToDoModel.Test/app.config +ToDoModel/app.config +UtilitiesCS.Test/app.config +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t10-poshqc-format.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t11-poshqc-analyze.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t12-poshqc-test.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t13-budget-baseline.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t2-base-anchor.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t3-ac-precondition.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t4-fizzler-census.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t5-shared-string-census.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t6-unsafe-census.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t7-encoding-baseline.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t8-known-debt-remeasure.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/p0-t9-linecount-baseline.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/baseline/phase0-instructions-read.2026-10-02T03-08.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/preflight-clearance.2026-10-02T03-45.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t1-module-authored.2026-10-02T03-18.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t10-taskvisualization-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t11-taskvisualization-test-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t12-todomodel-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t13-todomodel-test-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t14-utilitiescs-test-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t16-sweep-verification.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t17-unsafe-unchanged.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t4-quickfiler-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t5-quickfiler-test-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t6-svgcontrol-test-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t7-tags-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t8-taskmaster-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p1-t9-tasktree-redirect.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t15-pass-after.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t2-tests-authored.2026-10-02T03-18.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md +docs/features/potential/promoted/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md +scripts/dependencies/BindingRedirectVerification.psm1 +tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 +``` + +Capture 2, `git status --porcelain --untracked-files=all` (verbatim): + +```text + M docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md + M scripts/dependencies/BindingRedirectVerification.psm1 + M tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter1.2026-10-02T03-29.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t1-poshqc-format.iter2.2026-10-02T03-33.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t2-poshqc-analyze.iter2.2026-10-02T03-35.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t3-poshqc-test.iter2.2026-10-02T03-38.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t4-loop-closure.2026-10-02T03-40.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t5-function-test-map.2026-10-02T03-41.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t6-file-size-audit.2026-10-02T03-42.md +?? docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t7-no-csharp-scope.2026-10-02T03-43.md +``` + +(The porcelain capture was taken before this artifact was written, so this artifact and its predecessors written afterwards are not in it; all of them lie under the feature folder, which is inside the Write Set.) + +Evaluation (C6): the evaluated set is the union of the two captures' paths minus the five `.claude/agent-memory/atomic-planner/` paths (removed by prefix) minus the P0-T2 INHERITED list (the preflight-clearance artifact, issue.md, plan.2026-10-02T00-16.md, the research artifact and `docs/features/potential/promoted/2026-08-04-stale-fizzler-and-unsafe-binding-redirects.md`, each listed in INHERITED). The remainder was compared against the Write Set union: the 11 Write Set configs, `scripts/dependencies/BindingRedirectVerification.psm1`, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and paths under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`. Every remaining path is in that union; no other path appears (no STOP: FOOTPRINT). The promoted potential entry is outside the Write Set but is an INHERITED path present before this item started (P0-T2), so it is subtracted, not an item change. + +Positive control: all 13 named source paths are present in the evaluated set: QuickFiler/app.config, QuickFiler.Test/app.config, SVGControl.Test/app.config, Tags/app.config, TaskMaster/app.config, TaskTree/app.config, TaskVisualization/app.config, TaskVisualization.Test/app.config, ToDoModel/app.config, ToDoModel.Test/app.config, UtilitiesCS.Test/app.config, scripts/dependencies/BindingRedirectVerification.psm1 and tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 (13 of 13). + +Acceptance: every evaluated path is inside the Write Set union; all 13 named source paths are present. + +Output Summary: Footprint equals the Write Set. 13 of 13 named source paths present; no path outside the Write Set after the agent-memory and INHERITED subtractions. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t9-ac1-checkoff.2026-10-02T03-47.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t9-ac1-checkoff.2026-10-02T03-47.md new file mode 100644 index 000000000..1a0ce0b59 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/qa-gates/p2-t9-ac1-checkoff.2026-10-02T03-47.md @@ -0,0 +1,16 @@ +# P2-T9 Check off AC1 + +Timestamp: 2026-10-02T03-47 +Command: Edit tool on `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (`- [ ] AC1:` to `- [x] AC1:`); Grep pattern `^- \[x\] AC1:` count mode over issue.md +EXIT_CODE: 0 + +Checked off AC: AC1 in issue.md (line 84), changing only `- [ ]` to `- [x]`; the criterion text after the marker is unchanged (the Edit old_string and new_string differed only in that marker). Grep count of `^- \[x\] AC1:` is 1. + +Evidence cited: + +- P1-T4 to P1-T14: eleven EDIT-FIZZLER edits, each a one-line change (numstat `1 1`), with `w/crlf` per CMD-EOL and BOM bytes preserved per file against the P0-T7 baseline. +- P1-T16: 13 occurrences of `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"` across 13 files and 11 changed `app.config` paths. +- P0-T7: baseline encoding observation (11 `w/crlf`, 11 BOM-BYTES, 11 equal count pairs) used as the preservation reference. +- P2-T7 control: the BASE_SHA-anchored diff over `'*/app.config'` lists exactly the 11 Write Set config paths. + +Output Summary: AC1 checked off in issue.md; Grep count of the checked line is 1; text unchanged. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md index a6b9b7b0d..dd7457ab5 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md @@ -81,12 +81,12 @@ The record above was captured 2026-08-04. The state on main at preparation time ## Acceptance Criteria -- [ ] AC1: All 13 `app.config` files that carry a Fizzler `bindingRedirect` read `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`; the 11 stale files (QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test) each change on that one line only, with the UTF-8 BOM and CRLF line endings preserved. -- [ ] AC2: All 17 `app.config` files still redirect `System.Runtime.CompilerServices.Unsafe` to `6.0.3.0` (already delivered by issue 929; verified, not edited). -- [ ] AC3: A new Pester-tested detector reports a `bindingRedirect` whose `newVersion` equals no csproj `Reference` version for that assembly name, with a negative control (fixture redirect to a version no `Reference` provides yields one finding), a positive control (matching fixture yields none), and an examined-entry count that guards against a vacuous zero-finding pass. -- [ ] AC4: A repository-level Pester test runs the detector over every `app.config` against every csproj `Reference` and asserts that the findings equal exactly the recorded known-debt set (15 assembly and version pairs, none of them Fizzler or Unsafe) and that the unverifiable names equal exactly the recorded set of 3; a regression of any Fizzler redirect, a new mismatch, or a stale known-debt entry fails the test. -- [ ] AC5: The Fizzler regression test fails before the 11 config edits and passes after them (fail-before evidence recorded). -- [ ] AC6: PoshQC format, analyze, and test report no errors, and every exported function of the new module is exercised by at least one Pester test for its positive, negative, and edge paths. The Pester line-coverage figure for the new module is produced by the CI Pester job (workflow floor 80 percent, repository rule 85 percent) and is not measured locally. +- [x] AC1: All 13 `app.config` files that carry a Fizzler `bindingRedirect` read `oldVersion="0.0.0.0-1.3.1.0" newVersion="1.3.1.0"`; the 11 stale files (QuickFiler, QuickFiler.Test, SVGControl.Test, Tags, TaskMaster, TaskTree, TaskVisualization, TaskVisualization.Test, ToDoModel, ToDoModel.Test, UtilitiesCS.Test) each change on that one line only, with the UTF-8 BOM and CRLF line endings preserved. +- [x] AC2: All 17 `app.config` files still redirect `System.Runtime.CompilerServices.Unsafe` to `6.0.3.0` (already delivered by issue 929; verified, not edited). +- [x] AC3: A new Pester-tested detector reports a `bindingRedirect` whose `newVersion` equals no csproj `Reference` version for that assembly name, with a negative control (fixture redirect to a version no `Reference` provides yields one finding), a positive control (matching fixture yields none), and an examined-entry count that guards against a vacuous zero-finding pass. +- [x] AC4: A repository-level Pester test runs the detector over every `app.config` against every csproj `Reference` and asserts that the findings equal exactly the recorded known-debt set (15 assembly and version pairs, none of them Fizzler or Unsafe) and that the unverifiable names equal exactly the recorded set of 3; a regression of any Fizzler redirect, a new mismatch, or a stale known-debt entry fails the test. +- [x] AC5: The Fizzler regression test fails before the 11 config edits and passes after them (fail-before evidence recorded). +- [x] AC6: PoshQC format, analyze, and test report no errors, and every exported function of the new module is exercised by at least one Pester test for its positive, negative, and edge paths. The Pester line-coverage figure for the new module is produced by the CI Pester job (workflow floor 80 percent, repository rule 85 percent) and is not measured locally. ## Next Step diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md index 068abdf90..90f79da97 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/plan.2026-10-02T00-16.md @@ -376,23 +376,23 @@ Describe 'Repository binding redirects (issue 953)' (reads tracked files read-on The loop is P2-T1, P2-T2, P2-T3 in order. If P2-T1 rewrote a file, or P2-T2 returned `ok` false, or P2-T3 reported any failure, the executor fixes only Write Set PowerShell files (a rewrite of a file outside the Write Set is pre-existing drift: record it, restore with `git checkout -- `, continue) and restarts at P2-T1 as iteration N+1 with `.iter` artifacts. P2-T4 closes the loop only when one iteration shows no rewrite, `ok` true and zero failures together. No step may be recorded SKIPPED. -- [ ] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (16 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. -- [ ] [P2-T2] Final QC analyze step (toolchain step 2) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal and the `GATE-SUBSTITUTION:` line. `ok` false: fix the new files only and restart at P2-T1. Type checking is not applicable to PowerShell (`.claude/rules/powershell.md` line 17) and is recorded as such in the artifact. Artifact `evidence/qa-gates/p2-t2-poshqc-analyze.iter..md`. -- [ ] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. -- [ ] [P2-T4] Loop closure record for the QC toolchain under `evidence/qa-gates/`: list every iteration run (P2-T1, P2-T2, P2-T3 artifact names) and name the terminal iteration N on which P2-T1 recorded `REWRITE-COUNT: 0`, P2-T2 `ok` true and P2-T3 `failures=0` together. Acceptance: the three terminal-iteration artifacts exist (Glob) and carry those values. Artifact `evidence/qa-gates/p2-t4-loop-closure..md`. -- [ ] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10), negative (11) and edge (12, one assembly at two versions across several project texts) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. -- [ ] [P2-T6] File-size audit over `scripts/dependencies/` and `tests/scripts/dependencies/` after the terminal format pass: CMD-LINECOUNT for the two new files and the four P0-T9 neighbours. Acceptance: `BindingRedirectVerification.psm1` below 500 (record the value with `TARGET-BAND: 100-150` and whether it lies inside), `BindingRedirectVerification.Tests.ps1` below 500, and the four neighbour counts equal their P0-T9 values (they are not edited). Markdown under the feature folder is exempt from the 500-line cap per `.claude/rules/general-code-change.md`. Artifact `evidence/qa-gates/p2-t6-file-size-audit..md`. -- [ ] [P2-T7] No-C#-toolchain scope proof over the worktree: `git diff --name-only -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'` prints nothing, paired with `git status --porcelain -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'` printing nothing (the leading `*` makes the pathspec match a packages.config in any project directory; a bare `packages.config` matches only the repository root). Acceptance: both empty; the artifact states `CSHARP-TOOLCHAIN: not applicable; no C# source, project, solution or manifest file changed (D11)`. Artifact `evidence/qa-gates/p2-t7-no-csharp-scope..md`. -- [ ] [P2-T8] Footprint assertion over the worktree (CMD-FOOTPRINT): the evaluated set (C6) must equal the union of the 11 Write Set configs, `scripts/dependencies/BindingRedirectVerification.psm1`, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` and paths under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`. Acceptance: every evaluated path is in that union (any other path is STOP: FOOTPRINT, never reverted by this executor) and, as the positive control, all 13 named source paths are present in the evaluated set. Both captures are recorded verbatim per C4; the agent-memory subtraction is stated by composition, not by count. Artifact `evidence/qa-gates/p2-t8-footprint..md`. -- [ ] [P2-T9] Check off AC1 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 84, `- [ ] AC1:` to `- [x] AC1:`) citing P1-T4 to P1-T14 (one-line change, `w/crlf`, BOM preserved per file), P1-T16 (13 at 1.3.1.0, 11 changed paths) and P0-T7. Acceptance: Grep count of `^- \[x\] AC1:` in issue.md is 1 and the criterion text after the marker is unchanged. Artifact `evidence/qa-gates/p2-t9-ac1-checkoff..md`. -- [ ] [P2-T10] Check off AC2 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 85) citing P0-T6 and P1-T17. Acceptance: Grep count of `^- \[x\] AC2:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t10-ac2-checkoff..md`. -- [ ] [P2-T11] Check off AC3 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 86) citing P1-T1, P1-T2 and the P2-T3 terminal pass of tests 1 (negative control), 2 (positive control) and 6 (examined count). Acceptance: Grep count of `^- \[x\] AC3:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t11-ac3-checkoff..md`. -- [ ] [P2-T12] Check off AC4 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 87) citing P0-T8, P1-T15 and the P2-T3 terminal pass of test 14 (15 pairs, none Fizzler or Unsafe, 3 unverifiable names). Acceptance: Grep count of `^- \[x\] AC4:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t12-ac4-checkoff..md`. -- [ ] [P2-T13] Check off AC5 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 88) citing P1-T3 (fail-before, `but got 11`) and P1-T15 (pass-after). Acceptance: Grep count of `^- \[x\] AC5:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t13-ac5-checkoff..md`. -- [ ] [P2-T14] Check off AC6 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 89) citing P2-T1 to P2-T5 and quoting the AC6 literal `is not measured locally` as the authority for the CI coverage deferral (D8). Acceptance: Grep count of `^- \[x\] AC6:` is 1 and the text is unchanged; the artifact carries `COVERAGE-SOURCE: CI`. Artifact `evidence/qa-gates/p2-t14-ac6-checkoff..md`. -- [ ] [P2-T15] Acceptance-criteria status summary and plan reconciliation for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: Grep count of `^- \[x\] AC[1-6]:` in issue.md is 6 and of `^- \[ \] AC[1-6]:` is 0; the artifact carries the `### Acceptance Criteria Status` block (Source, Total AC items: 6, Checked off: 6, Remaining: 0). Acceptance: both counts hold. Artifact `evidence/qa-gates/p2-t15-ac-status..md`. -- [ ] [P2-T16] Follow-up note for the coordinator under `evidence/other/`: the 15 known-debt pairs of section 7 with their config counts and Reference versions, the 3 unverifiable names, the statement that correcting them is out of scope for issue 953, and the statement that this plan created no issue and no potential entry (the coordinator promotes). Acceptance: the artifact lists all 15 pairs and 3 names and carries `FOLLOW-UP: known-debt redirect correction, 15 pairs, 137 entries; promotion by the coordinator`. Artifact `evidence/other/p2-t16-known-debt-followup..md`. -- [ ] [P2-T17] Reduced-audit handoff index under `evidence/other/` for the QC and test evidence: `BASE_SHA:`, the Write Set, `COVERAGE-SOURCE: CI`, every `GATE-SUBSTITUTION:` line used, the budget outcome (no denial, or the denial record), the terminal loop iteration N, the final suite counts, and the path of every artifact written by P0-T1 through P2-T16 (bounded there; this artifact names itself only as the index). The index cites D8 as this plan's exception to the atomic-plan-contract Coverage Evidence Contract: no numeric baseline or post-change coverage figure is recorded locally because no local Pester coverage route instruments `scripts/dependencies`, and the figure is read from the CI Pester job. Acceptance: every artifact path named by P0-T1 through P2-T16 exists (Glob) and is listed; the index carries the literals `AUDIT-MODE: reduced (minor-audit)` and `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`. Artifact `evidence/other/p2-t17-reduced-audit-handoff..md`. +- [x] [P2-T1] Final QC format step (toolchain step 1) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-HASHSET, CMD-POSHQC-FORMAT, CMD-HASHSET. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal; the before and after hash sets are identical (16 entries, both new files present); the artifact records `REWRITE-COUNT: 0`. On a non-terminal iteration a differing Write Set hash is recorded as `REWRITE: ` and the loop restarts. Artifact `evidence/qa-gates/p2-t1-poshqc-format.iter..md`. +- [x] [P2-T2] Final QC analyze step (toolchain step 2) over `scripts/dependencies` and `tests/scripts/dependencies`: CMD-POSHQC-ANALYZE. Acceptance on the terminal iteration: `ok` true with the 2-folder summary literal and the `GATE-SUBSTITUTION:` line. `ok` false: fix the new files only and restart at P2-T1. Type checking is not applicable to PowerShell (`.claude/rules/powershell.md` line 17) and is recorded as such in the artifact. Artifact `evidence/qa-gates/p2-t2-poshqc-analyze.iter..md`. +- [x] [P2-T3] Final QC test step (toolchain step 4) over `tests/scripts/dependencies`: CMD-JUNIT-DELETE, CMD-POSHQC-TEST, CMD-JUNIT-READ (JUNIT lines transcribed under the CMD-JUNIT-READ long-line rule). Acceptance on the terminal iteration: `ok` true; JUNIT-ROOT `failures=0`; exactly 9 JUNIT-SUITE lines; `BindingRedirectVerification.Tests.ps1 tests=14 failures=0 skipped=0`; every other suite equals its P0-T12 `BASELINE-SUITE` count with `failures=0 skipped=0`; `JUNIT-NOTPASSED: none` (recorded only when CMD-JUNIT-READ (a) matched exactly one line in this task; a no-match from (a) is a failed read and the task is not complete); the `COVERAGE-MEASUREMENT:` literal line is present and no numeric coverage percentage appears in the artifact. `EXIT_CODE: 0`. Artifact `evidence/qa-gates/p2-t3-poshqc-test.iter..md`. +- [x] [P2-T4] Loop closure record for the QC toolchain under `evidence/qa-gates/`: list every iteration run (P2-T1, P2-T2, P2-T3 artifact names) and name the terminal iteration N on which P2-T1 recorded `REWRITE-COUNT: 0`, P2-T2 `ok` true and P2-T3 `failures=0` together. Acceptance: the three terminal-iteration artifacts exist (Glob) and carry those values. Artifact `evidence/qa-gates/p2-t4-loop-closure..md`. +- [x] [P2-T5] Per-function test enumeration for `scripts/dependencies/BindingRedirectVerification.psm1` (AC6 second clause): read the module's `Export-ModuleMember` list and `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`, and record for `Find-StaleBindingRedirect` the positive-path tests (2, 3, 5, 8, 13, 14), negative-path tests (1, 4, 9), edge-path tests (6, 7) and for `ConvertTo-ReferenceVersionMap` the positive (10), negative (11) and edge (12, one assembly at two versions across several project texts) tests, each by its verbatim It name. Acceptance: both exported names appear with at least one positive, one negative and one edge It each, and every cited It name is present in the test file (Grep `-F`, count 1). Artifact `evidence/qa-gates/p2-t5-function-test-map..md`. +- [x] [P2-T6] File-size audit over `scripts/dependencies/` and `tests/scripts/dependencies/` after the terminal format pass: CMD-LINECOUNT for the two new files and the four P0-T9 neighbours. Acceptance: `BindingRedirectVerification.psm1` below 500 (record the value with `TARGET-BAND: 100-150` and whether it lies inside), `BindingRedirectVerification.Tests.ps1` below 500, and the four neighbour counts equal their P0-T9 values (they are not edited). Markdown under the feature folder is exempt from the 500-line cap per `.claude/rules/general-code-change.md`. Artifact `evidence/qa-gates/p2-t6-file-size-audit..md`. +- [x] [P2-T7] No-C#-toolchain scope proof over the worktree: `git diff --name-only -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'` prints nothing, paired with `git status --porcelain -- '*.cs' '*.csproj' '*.sln' '*.props' '*.targets' '*packages.config'` printing nothing (the leading `*` makes the pathspec match a packages.config in any project directory; a bare `packages.config` matches only the repository root). Acceptance: both empty; the artifact states `CSHARP-TOOLCHAIN: not applicable; no C# source, project, solution or manifest file changed (D11)`. Artifact `evidence/qa-gates/p2-t7-no-csharp-scope..md`. +- [x] [P2-T8] Footprint assertion over the worktree (CMD-FOOTPRINT): the evaluated set (C6) must equal the union of the 11 Write Set configs, `scripts/dependencies/BindingRedirectVerification.psm1`, `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` and paths under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`. Acceptance: every evaluated path is in that union (any other path is STOP: FOOTPRINT, never reverted by this executor) and, as the positive control, all 13 named source paths are present in the evaluated set. Both captures are recorded verbatim per C4; the agent-memory subtraction is stated by composition, not by count. Artifact `evidence/qa-gates/p2-t8-footprint..md`. +- [x] [P2-T9] Check off AC1 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 84, `- [ ] AC1:` to `- [x] AC1:`) citing P1-T4 to P1-T14 (one-line change, `w/crlf`, BOM preserved per file), P1-T16 (13 at 1.3.1.0, 11 changed paths) and P0-T7. Acceptance: Grep count of `^- \[x\] AC1:` in issue.md is 1 and the criterion text after the marker is unchanged. Artifact `evidence/qa-gates/p2-t9-ac1-checkoff..md`. +- [x] [P2-T10] Check off AC2 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 85) citing P0-T6 and P1-T17. Acceptance: Grep count of `^- \[x\] AC2:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t10-ac2-checkoff..md`. +- [x] [P2-T11] Check off AC3 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 86) citing P1-T1, P1-T2 and the P2-T3 terminal pass of tests 1 (negative control), 2 (positive control) and 6 (examined count). Acceptance: Grep count of `^- \[x\] AC3:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t11-ac3-checkoff..md`. +- [x] [P2-T12] Check off AC4 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 87) citing P0-T8, P1-T15 and the P2-T3 terminal pass of test 14 (15 pairs, none Fizzler or Unsafe, 3 unverifiable names). Acceptance: Grep count of `^- \[x\] AC4:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t12-ac4-checkoff..md`. +- [x] [P2-T13] Check off AC5 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 88) citing P1-T3 (fail-before, `but got 11`) and P1-T15 (pass-after). Acceptance: Grep count of `^- \[x\] AC5:` is 1 and the text is unchanged. Artifact `evidence/qa-gates/p2-t13-ac5-checkoff..md`. +- [x] [P2-T14] Check off AC6 in `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/issue.md` (line 89) citing P2-T1 to P2-T5 and quoting the AC6 literal `is not measured locally` as the authority for the CI coverage deferral (D8). Acceptance: Grep count of `^- \[x\] AC6:` is 1 and the text is unchanged; the artifact carries `COVERAGE-SOURCE: CI`. Artifact `evidence/qa-gates/p2-t14-ac6-checkoff..md`. +- [x] [P2-T15] Acceptance-criteria status summary and plan reconciliation for `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/`: Grep count of `^- \[x\] AC[1-6]:` in issue.md is 6 and of `^- \[ \] AC[1-6]:` is 0; the artifact carries the `### Acceptance Criteria Status` block (Source, Total AC items: 6, Checked off: 6, Remaining: 0). Acceptance: both counts hold. Artifact `evidence/qa-gates/p2-t15-ac-status..md`. +- [x] [P2-T16] Follow-up note for the coordinator under `evidence/other/`: the 15 known-debt pairs of section 7 with their config counts and Reference versions, the 3 unverifiable names, the statement that correcting them is out of scope for issue 953, and the statement that this plan created no issue and no potential entry (the coordinator promotes). Acceptance: the artifact lists all 15 pairs and 3 names and carries `FOLLOW-UP: known-debt redirect correction, 15 pairs, 137 entries; promotion by the coordinator`. Artifact `evidence/other/p2-t16-known-debt-followup..md`. +- [x] [P2-T17] Reduced-audit handoff index under `evidence/other/` for the QC and test evidence: `BASE_SHA:`, the Write Set, `COVERAGE-SOURCE: CI`, every `GATE-SUBSTITUTION:` line used, the budget outcome (no denial, or the denial record), the terminal loop iteration N, the final suite counts, and the path of every artifact written by P0-T1 through P2-T16 (bounded there; this artifact names itself only as the index). The index cites D8 as this plan's exception to the atomic-plan-contract Coverage Evidence Contract: no numeric baseline or post-change coverage figure is recorded locally because no local Pester coverage route instruments `scripts/dependencies`, and the figure is read from the CI Pester job. Acceptance: every artifact path named by P0-T1 through P2-T16 exists (Glob) and is listed; the index carries the literals `AUDIT-MODE: reduced (minor-audit)` and `COVERAGE-EXCEPTION: D8 (no local Pester coverage route instruments scripts/dependencies)`. Artifact `evidence/other/p2-t17-reduced-audit-handoff..md`. ## 11. Traceability diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md index 7724029c3..329dcfdff 100644 --- a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/research/2026-10-02T00-35-fizzler-redirect-remedy-and-redirect-gate-research.md @@ -38,7 +38,7 @@ Grep of `System.Runtime.CompilerServices.Unsafe` -A1 over `**/app.config` return - `SVGControl/packages.config:4` and `UtilitiesCS/packages.config:11`: `Fizzler` version `1.3.1`. - No other csproj or packages.config mentions Fizzler. The other 11 configs name Fizzler only through the redirect; those projects receive Fizzler.dll transitively by project reference (copy-local) [inference from UtilitiesCS being the common dependency; [U] not built here]. - `using Fizzler;` appears in `SVGControl/PictureBoxSVG.cs:15`, `UtilitiesCS/.../Triage_OlLogic.cs:9`, `.../BayesianClassifier.cs:14`, `.../MailItemHelper.cs:11`. The #418 research (`docs/features/archive/2026-08-04-svg-renderer-null-document-nre-418/research/2026-08-04T15-05-svg-renderer-null-document-research.md:94,270-276`) records that Svg/ExCSS carry no Fizzler AssemblyRef and the usings are unused, so the redirects are inert today. Not re-verified here (no binaries in the worktree). -- Packages tree: absent in the worktree (Glob `packages/*` returned nothing). Main checkout (read-only): `C:\Users\DanMoisan\repos\TaskMaster\packages\Fizzler.1.3.1\lib\netstandard2.0\Fizzler.dll` and `netstandard1.0` exist; also `Svg.3.4.8`, `ExCSS.4.3.2`, `System.Runtime.CompilerServices.Unsafe.6.1.2`. +- Packages tree: absent in the worktree (Glob `packages/*` returned nothing). Main checkout (read-only): `\packages\Fizzler.1.3.1\lib\netstandard2.0\Fizzler.dll` and `netstandard1.0` exist; also `Svg.3.4.8`, `ExCSS.4.3.2`, `System.Runtime.CompilerServices.Unsafe.6.1.2`. ### 1.4 Binding-redirect autogeneration [V] diff --git a/scripts/dependencies/BindingRedirectVerification.psm1 b/scripts/dependencies/BindingRedirectVerification.psm1 index 8c1539be3..620bc8237 100644 --- a/scripts/dependencies/BindingRedirectVerification.psm1 +++ b/scripts/dependencies/BindingRedirectVerification.psm1 @@ -110,7 +110,7 @@ function Find-StaleBindingRedirect { $examined++ $deployed = @(& $DeployedVersionProvider $record.Name | Where-Object { -not [string]::IsNullOrEmpty([string]$_) } | - ForEach-Object { [string]$_ }) + ForEach-Object { [string]$_ }) if ($deployed.Count -eq 0) { if (-not $unverifiable.Contains($record.Name)) { $unverifiable.Add($record.Name) } continue diff --git a/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 b/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 index 8a20728b4..5f67da131 100644 --- a/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 +++ b/tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1 @@ -248,7 +248,7 @@ Describe 'Repository binding redirects (issue 953)' { $configPath = @( Get-ChildItem -LiteralPath $script:RepoRoot -Directory | ForEach-Object { Join-Path $_.FullName 'app.config' } | - Where-Object { Test-Path -LiteralPath $_ } + Where-Object { Test-Path -LiteralPath $_ } ) $configPath.Count | Should -BeGreaterThan 9 @@ -278,12 +278,12 @@ Describe 'Repository binding redirects (issue 953)' { $configPath = @( $rootDirectory | ForEach-Object { Join-Path $_.FullName 'app.config' } | - Where-Object { Test-Path -LiteralPath $_ } + Where-Object { Test-Path -LiteralPath $_ } ) $projectPath = @( $rootDirectory | ForEach-Object { Get-ChildItem -LiteralPath $_.FullName -Filter '*.csproj' -File } | - ForEach-Object { $_.FullName } + ForEach-Object { $_.FullName } ) $configPath.Count | Should -BeGreaterThan 9 $projectPath.Count | Should -BeGreaterThan 9 From 119888f0efc69732402f1fb3c034c53a05c42965 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 03:43:41 -0400 Subject: [PATCH 13/14] docs(953): add reduced-audit code review, policy audit and feature audit Co-Authored-By: Claude Sonnet 5.5 --- .../code-review.2026-10-02T04-05.md | 77 +++++++++++ .../feature-audit.2026-10-02T04-05.md | 45 +++++++ .../policy-audit.2026-10-02T04-05.md | 122 ++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/code-review.2026-10-02T04-05.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/feature-audit.2026-10-02T04-05.md create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/policy-audit.2026-10-02T04-05.md diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/code-review.2026-10-02T04-05.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/code-review.2026-10-02T04-05.md new file mode 100644 index 000000000..9fe9aa523 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/code-review.2026-10-02T04-05.md @@ -0,0 +1,77 @@ +# Code Review (reduced audit, minor-audit): issue 953 + +Timestamp: 2026-10-02T04-05 +Scope: full branch diff, base `860d67bf4fddecb929e0d6c166065fd1ee752feb` to head `2449cdd34`. +Worktree: `` + +## Executive Summary + +Verdict: PASS with 0 blocking findings and 4 non-blocking observations. + +The change moves 11 Fizzler binding redirects from 1.3.0.0 to 1.3.1.0 (one line each), adds a 139-line detector module and a 335-line Pester file. The module is pure over text, takes the deployed-version source as an injected scriptblock, and has a small exported surface (2 functions). The tests use in-memory fixtures plus read-only reads of tracked files; no temporary files, no `$TestDrive`, no sleeps. The ratchet test can fail on each of the three required conditions (see Ratchet Analysis). + +## Acceptance Criteria Inventory + +Source: `issue.md` `## Acceptance Criteria` (work mode minor-audit). AC1 to AC6, all checked. Evaluation is in `feature-audit.2026-10-02T04-05.md`. + +## Findings Table + +| ID | Severity | File:Line | Finding | Evidence | Recommendation | Blocking | +|---|---|---|---|---|---|---| +| CR-1 | Low | tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1:205-211 | The non-configuration-text test asserts `Should -Throw` with no message or error-id match, so any exception (including a parameter-binding fault) satisfies it. | Line 210 `$act \| Should -Throw`. | Add `-ExpectedMessage` or `-ErrorId` for the parser rejection. | No | +| CR-2 | Low | scripts/dependencies/BindingRedirectVerification.psm1:40-41,107 and Tests.ps1:180-192,214-242 | Two documented edge behaviours have no test: whitespace-only `AppConfigText` (only `''` is tested) and `ConvertTo-ReferenceVersionMap` with an empty collection or an empty-string element (documented as rejected by the parser). | Module doc comments lines 40-41 and 83-85; no matching `It`. | Add one `It` each. Line coverage is likely unaffected (same branches); this is scenario completeness. | No | +| CR-3 | Low | tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1:253,271,288-289 | The repository-level tests hard-code `13` Fizzler configs and `-BeGreaterThan 9` file counts. A legitimate new project config with a Fizzler redirect fails test 13 until the literal is edited. | Lines 253, 271. | Accepted by design as a ratchet; keep the `-Because` text. No change required. | No | +| CR-4 | Low | tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1:275-334 | The known-debt test bundles five assertions (examined parity, debt equality, unverifiable equality, Fizzler absence, Unsafe absence) in one `It`, against the rule of one behavior per `It`. The plan specified this shape and the `-Because` messages localize each failure. | Lines 329-333. | Optional split into separate `It` blocks sharing a `BeforeAll` result. | No | + +No Medium, High or Blocking findings. + +## Detailed Evaluation + +### File-size cap (general-code-change.md) + +- `scripts/dependencies/BindingRedirectVerification.psm1`: 139 lines. PASS. +- `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`: 335 lines. PASS. +- Evidence: `evidence/qa-gates/p2-t6-file-size-audit.2026-10-02T03-42.md`; both files read in full by this review. + +### Temporary files and `$TestDrive` + +PASS. A Grep of the test file for `TestDrive`, `New-TemporaryFile`, `GetTempPath`, `$env:TEMP`, `Set-Content`, `Out-File` and `Start-Sleep` returned no match. Repository-level tests call `[System.IO.File]::ReadAllText` on tracked paths derived from `$PSScriptRoot` and write nothing (test header comment lines 8-9). + +### Arrange-Act-Assert and naming + +PASS. Every `It` carries Arrange, Act, Assert comments or a combined "Arrange and Act" comment; names state the scenario and expected outcome. Two tests (lines 216-233, 235-241) combine Arrange and Act on one comment, which is acceptable. + +### Scenario completeness + +PASS with CR-2. + +| Function | Positive | Negative | Edge | +|---|---|---|---| +| Find-StaleBindingRedirect | line 118 (current redirect), 131 (oldVersion ignored), 194 (multi-version) | line 102 (stale redirect), 205 (non-config text) | line 142 (unverifiable), 156 (no redirect), 169 (count), 180 (empty text) | +| ConvertTo-ReferenceVersionMap | line 216 | line 226 (no Version omitted) | line 235 (union across texts) | + +### Ratchet test ability to fail + +PASS. Test 14 (lines 275-334) must fail on: + +1. A new mismatch: `$actualDebt` gains an entry absent from `$expectedDebt`; `Should -Be` array equality (line 331) fails. Directly demonstrated for the Fizzler pair in the fail-before run (`evidence/regression-testing/p1-t3-fail-before.2026-10-02T03-21.md`, observed list of 16 entries including `Fizzler|1.3.0.0`). +2. A Fizzler regression: test 13 (lines 246-273) fails with the per-config message (demonstrated: `but got 11`), and test 14 line 333 fails on any `Fizzler|*` finding. +3. A stale known-debt entry: `$expectedDebt` retains a pair that no longer appears in `$actualDebt`; the same equality at line 331 fails. This path is not demonstrated by a separate recorded run; it follows from the symmetric array comparison. The follow-up note (`evidence/other/p2-t16-known-debt-followup.2026-10-02T03-54.md`) states the consequence explicitly. + +Vacuity guard: line 330 requires `$examined` equal to the raw ` promoted}/...` rename is a lifecycle artifact and not plan work. + +## Follow-ups owed + +- Promote the 15-pair known-debt correction (137 entries, 3 unverifiable names) through the potential-entry lifecycle (`p2-t16`); the coordinator owns this. diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/policy-audit.2026-10-02T04-05.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/policy-audit.2026-10-02T04-05.md new file mode 100644 index 000000000..cff97c52c --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/policy-audit.2026-10-02T04-05.md @@ -0,0 +1,122 @@ +# Policy Audit (reduced audit, minor-audit): issue 953 + +Timestamp: 2026-10-02T04-05 + +## Template Resolution Deviation + +The policy-audit template MCP asset resolver is not in this agent's tool set. This document is hand-authored with the canonical headings. PR-context artifacts were not regenerated; scope was derived from `git -C diff --numstat 860d67bf4fddecb929e0d6c166065fd1ee752feb 2449cdd34` and the plan Write Set. + +## Rejected Scope Narrowing + +None. The caller prompt asked for a reduced audit; the full branch-versus-base diff was audited. The caller instruction not to mark coverage FAIL for lack of a local figure is a legitimate statement of where the figure comes from (CI Pester job, `COVERAGE-SOURCE: CI`), not a narrowing; the PowerShell verdict is recorded as pending CI rather than as a pass. + +## Executive Summary + +Overall verdict: PASS with one item pending CI. 0 blocking findings. + +- PowerShell is the only language with changed code (2 files). The 11 `app.config` edits are configuration, not code. +- The PowerShell line-coverage figure (floor 85 percent) is produced by the CI Pester job and is pending. The orchestrator records it. +- No temporary files, no `$TestDrive`, no absolute host paths in committed evidence, both files under 500 lines, Write Set matches the plan, Unsafe redirects untouched. + +## 1. Policy Compliance Summary + +### 1.1 Policies evaluated + +| Policy | Verdict | Evidence | +|---|---|---| +| CLAUDE.md (policy order, tonality) | PASS | Artifacts neutral; no emoji or hyperbole. | +| general-code-change.md (500-line cap, error handling, I/O seam) | PASS | Module 139 lines, tests 335 lines (`evidence/qa-gates/p2-t6-file-size-audit.2026-10-02T03-42.md`); parser errors propagate; scriptblock seam for provider. | +| general-unit-test.md (AAA, no temp files, scenarios, location) | PASS | Test at `tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1` mirrors `scripts/dependencies/`; no `TestDrive`/temp APIs (Grep over the file returned no match); AAA comments present. | +| powershell.md (advanced functions, Pester v5, toolchain order) | PASS | `CmdletBinding`, mandatory params, StrictMode; format iter2, analyze, test recorded under `evidence/qa-gates/p2-t1..p2-t4`. | +| powershell.md (change budget) | PASS | 1 production file and 1 test file (within 2 and 3 caps); `evidence/baseline/p0-t13-budget-baseline`, `p2-t17` reports no denial. | +| quality-tiers.md / coverage | PENDING CI | See section 1.2. | +| tonality.md | PASS | Feature-folder artifacts and this audit use plain wording. | + +### 1.2 Coverage + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|---|---|---|---|---|---|---| +| PowerShell | 2 (1 production, 1 test) | 14 new (151 total) | PASS (0 failures) | N/A - new module, no baseline | N/A - pending CI Pester job | N/A - pending CI Pester job | +| C# | 0 | 0 | N/A | N/A | N/A | N/A | +| TypeScript | 0 | 0 | N/A | N/A | N/A | N/A | +| Python | 0 | 0 | N/A | N/A | N/A | N/A | + +Coverage source: `COVERAGE-SOURCE: CI` (pending). No local Pester route instruments `scripts/dependencies` (plan exception D8, recorded in `evidence/other/p2-t17-reduced-audit-handoff.2026-10-02T03-56.md`). The orchestrator records the CI run ID, head SHA and the per-file figure for `scripts/dependencies/BindingRedirectVerification.psm1` from the `pester-coverage` artifact. Required: line coverage at least 85 percent (repository rule; workflow floor 80 percent); no branch threshold applies to PowerShell. Both exported functions are exercised on positive, negative and edge paths (`p2-t5-function-test-map`), so a high line figure is expected, but this review states no figure. + +### 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: `N/A - new module` +- PowerShell post-change coverage artifact: `CI Pester job artifact pester-coverage, 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 + +### 1.2.1 Per-Language Coverage Comparison + +- PowerShell: Baseline: N/A. Post-change: N/A. Change: new module, figure produced by the CI Pester job. Disposition: INCOMPLETE. Evidence: `COVERAGE-SOURCE: CI` pending; `evidence/other/p2-t17-reduced-audit-handoff.2026-10-02T03-56.md`. +- C#: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero C# source files changed (only `app.config` redirects; `evidence/qa-gates/p2-t7-no-csharp-scope`). +- 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. + +### 1.2.2 Coverage Artifact State + +| Language | Coverage artifact | State | +|---|---|---| +| PowerShell | CI `pester-coverage` JaCoCo | Pending; orchestrator records run ID and head SHA | + +## 2. Evidence Location Compliance + +PASS. All evidence lives under `docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/{baseline,qa-gates,regression-testing,other}/`. The diff contains no file under `artifacts/baselines/`, `artifacts/qa/`, `artifacts/evidence/` or `artifacts/coverage/`. `validate_evidence_locations.py` was not run (Bash restricted to git); the path check was made from the numstat listing. + +## 3. Host-Path and Identity Hygiene + +PASS. A Grep over the feature folder for drive-letter paths, `DanMoisan`, `/Users/` and the worktree directory name returned no match; evidence uses ``. The four `.claude/agent-memory/atomic-planner/*953*` files also returned no match. + +## 4. Configuration Edit Integrity + +- 11 files at 1 insertion and 1 deletion (numstat): PASS. +- Footprint equals plan section 5 once `.claude/agent-memory/**` (Convention C6) and the inherited potential-entry move are removed: PASS. +- Unsafe redirects untouched (17 at 6.0.3.0; no diff line mentions Unsafe): PASS. +- CRLF and BOM preservation: PASS on executor evidence (`ls-files --eol` reports `w/crlf`, BOM bytes `239,187,191`, per file in `p1-t4` to `p1-t14`). This reviewer's own CR-anchored Grep returned no match, which reflects the search tool's handling of CRLF and not a defect; no byte-level independent check was possible under the Bash restriction. Residual risk: low (a lost CR would also add diff noise beyond one line only if the whole file were rewritten, which numstat 1/1 excludes). + +## 5. Toolchain Evidence + +- Format: iteration 1 rewrote the two new files (indentation), iteration 2 rewrote none (`p2-t1-poshqc-format.iter1`, `.iter2`). +- Analyze: `PoshQC analyze: pass (0 findings); tool reports no count` (`p2-t2-poshqc-analyze.iter2`). +- Test: 9 suites, 151 tests, 0 failures (`p2-t3-poshqc-test.iter2`); fail-before run: 2 failures (`p1-t3-fail-before`). +- Loop closure: terminal iteration 2 (`p2-t4-loop-closure`). C# toolchain not applicable (no C# source change). + +## 6. Findings and Remediation Triggers + +- Blocking: none. +- Non-blocking: see code-review CR-1 to CR-4. +- Remediation trigger: none. Pending item for the orchestrator: record the CI PowerShell line-coverage figure for `scripts/dependencies/BindingRedirectVerification.psm1`; if it is below 85 percent, that becomes a remediation trigger at that time. + +## 7. Verdict Table + +| Item | Verdict | +|---|---| +| Policy order and tone | PASS | +| File-size cap | PASS | +| No temp files or TestDrive | PASS | +| AAA and naming | PASS | +| Scenario completeness | PASS (CR-2 note) | +| Ratchet fails on new mismatch, Fizzler regression, stale entry | PASS | +| No absolute host paths in evidence | PASS | +| Write Set footprint | PASS | +| Unsafe untouched | PASS | +| PowerShell line coverage | PENDING CI (not FAIL, not PASS) | + +## Appendix A: Test Inventory + +`tests/scripts/dependencies/BindingRedirectVerification.Tests.ps1`: 14 tests. Describe `Find-StaleBindingRedirect (in-memory fixtures)` 9 tests (lines 102-211); Describe `ConvertTo-ReferenceVersionMap (in-memory fixtures)` 3 tests (216-241); Describe `Repository binding redirects (issue 953)` 2 tests (246, 275). + +## Appendix B: Toolchain Commands Reference + +PoshQC MCP tools: `run_poshqc_format`, `run_poshqc_analyze`, `run_poshqc_test` with the item worktree as workspace root and `scan_folders` `["tests/scripts/dependencies"]`. Pester coverage: CI `_pester.yml` job, artifact `pester-coverage`. From 0c70a51ca42ff922556e2c5b331b97192b22d158 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 03:53:40 -0400 Subject: [PATCH 14/14] docs(953): record CI Pester coverage for the redirect verification module Co-Authored-By: Claude Sonnet 5.5 --- .../other/ci-pester-coverage.2026-10-02T07-55.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/ci-pester-coverage.2026-10-02T07-55.md diff --git a/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/ci-pester-coverage.2026-10-02T07-55.md b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/ci-pester-coverage.2026-10-02T07-55.md new file mode 100644 index 000000000..75de68970 --- /dev/null +++ b/docs/features/active/2026-08-04-stale-fizzler-and-unsafe-binding-redirects-953/evidence/other/ci-pester-coverage.2026-10-02T07-55.md @@ -0,0 +1,13 @@ +# CI Pester coverage record for issue 953 (AC6 coverage source) + +Timestamp: 2026-10-02T07-55 +Command: gh run view 36980297940 --job 110753053226 --log (Grep of the PESTER and COVERAGE summary lines); gh run download 36980297940 --name pester-coverage (JaCoCo document, class counters for scripts/dependencies/BindingRedirectVerification) +EXIT_CODE: 0 +Output Summary: +- COVERAGE-SOURCE: CI (AC6: line coverage "is not measured locally"). +- CI run ID: 36980297940 (pull request 974). Head SHA measured: 119888f0efc69732402f1fb3c034c53a05c42965. +- Pester job result: PESTER Passed=393 Failed=0 Skipped=0 Total=393. +- Whole-folder line coverage printed by the job: COVERAGE LinePercent=94.63 Covered=1762 Total=1862 (gate floor 80 in _pester.yml; repository rule 85). +- Per-file, from the JaCoCo artifact pester-coverage, class scripts/dependencies/BindingRedirectVerification: LINE missed=0 covered=41 (100 percent); INSTRUCTION missed=0 covered=52 (100 percent). Per-method LINE: script body 4 of 4, ConvertTo-ReferenceVersionMap 14 of 14, Find-StaleBindingRedirect 23 of 23. +- New-code target of 90 percent (CLAUDE.md UT2): met. Repository floor of 85 percent: met. +- Other jobs on the same run: actionlint, build-analyzers, build-nullable, format-check, hygiene, mstest-coverage all pass.