Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/agent-memory/atomic-planner/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
- [#307 F2 ScoCollection deletion gate](project_307_f2_scocollection_deletion_gate.md) — full first-party ScoCollection/ScoStack reference set incl. tests beyond spec §7; ISubjectMapSco/IScoCollection F5 boundary; FS/Prompt seams live in ScoCollection.cs
- [#328 store-exclusion seams](project_328_store_exclusion_seams.md) — StoresWrapper(469)/TreeOfToDoItems(481) near 500-limit, ToDoEvents(594) pre-existing over-limit; new test .cs need csproj wiring; four inclusion surfaces lockstep; adopted persisted StoreWrapper.StoreId
- [C# coverage gate expects JaCoCo](project_csharp_coverage_gate_jacoco_format.md) — validate-feature-review-coverage.ps1 reads artifacts/csharp/coverage.xml as JaCoCo, not Cobertura; plan a conversion scoped to first-party
- [Durable script copy into feature folder](durable-script-copy-into-feature-folder.md) — copy scratchpad-supplied scripts into `<FEATURE>/scripts/` before referencing them in plan tasks (session-scoped temp paths aren't durable)
- [#351 QuickFiler breadcrumb plan seams](project_351_quickfiler_breadcrumb_plan_seams.md) — JSON code in UtilitiesCS only (QuickFiler lacks Newtonsoft); P2-T1 blocked-if-9101-absent; evidence/repro/ rejected; coordinator pattern
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: durable-script-copy-into-feature-folder
description: When a delegation prompt supplies an implementation script located under a session-scoped scratchpad temp directory, copy it into <FEATURE>/scripts/ before referencing it in plan tasks
metadata:
type: feedback
---

A calling agent (issue #354, stale-app-config-binding-redirects) supplied a proven-correct audit/fix script at a path under `AppData\Local\Temp\claude\...\<session-guid>\scratchpad\`. That path is tied to the current session and is not guaranteed to exist when `atomic-executor` (or a resumed/later session) actually executes the plan.

**Why:** Atomic tasks require explicit, durable file paths (per `atomic-plan-contract`). A path inside a session-specific scratchpad directory is ephemeral infrastructure, not a repo artifact, so a plan that references it verbatim risks becoming unexecutable the moment the authoring session ends.

**How to apply:** Before writing a plan task that runs a script supplied via a scratchpad/temp path, read the script's full contents and `Write` an identical copy into `<FEATURE>/scripts/<name>.py` (or another durable in-repo location appropriate to the feature). Reference the durable copy's repo-relative path in the plan task instead of the scratchpad path. This applies whether the script is a one-off audit tool, a fix script, or similar — anything the calling agent hands off from outside the repo tree.

Related: [[evidence-path-normalization]] (durable evidence-path handling follows the same "canonicalize before writing tasks" principle).
5 changes: 4 additions & 1 deletion .claude/agent-memory/feature-review/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
- [PR-context MCP unavailable, manual fallback](project_pr-context-mcp-unavailable-manual-fallback.md) — #269: no collect_pr_context tool in-session; hand-author pr_context.summary.txt from `git diff --numstat` in the hook's bullet format so Get-ChangedLanguageSet still fires; paths with spaces break that regex per-line
- [C# coverage: convert raw .coverage when canonical XML absent](project_csharp-coverage-independent-verification-via-raw-coverage-conversion.md) — #278: `dotnet-coverage merge -f cobertura` on the executor's own `TestResults/*.coverage` gives independent per-line verification without rerunning tests
- [coverage hook forces FAIL below 85% even with exemption](coverage-hook-forces-fail-below-floor-despite-exemption.md) — #283: below-floor language row must read "FAIL, dispositioned non-blocking via exemption" not PASS; C# Cobertura reads $null (skipped); re-added summary lines need `(+N/-N)` format or C# is skipped
- [coverage hook label regex matches substrings](project_coverage-hook-label-substring-false-positive.md) — #309: "csharpier"/"dotnet-coverage" prose anywhere in policy-audit.md can become a scanned coverage row via unanchored "csharp"/"dotnet" substring match; simulate the hook's regex against the draft before finalizing
- [coverage hook label regex matches substrings](project_coverage-hook-label-substring-false-positive.md) — #309/#354 R4: any language's label word (not just "csharp"/"dotnet") plus "coverage" plus a narrowing phrase on one line trips a false FAIL, including inside the boilerplate `## Rejected Scope Narrowing` paragraph; simulate the hook's regex (or the full validator) against the draft before finalizing
- [deletion-only PR with absent canonical C# artifact](project_deletion-only-pr-absent-coverage-artifact-309.md) — #309: pure-deletion, zero-new-files PR still gets FAIL C# coverage verdict per mandatory artifact-absence rule even though feature-evidence Cobertura shows the touched module (88.19%→88.23%) clears floor with zero regressions; disposition as procedural remediation, not a code defect
- [canonical JaCoCo includes un-instrumented assemblies](project_csharp-canonical-jacoco-includes-uninstrumented-assemblies.md) — #328: a canonical artifacts/csharp/coverage.xml aggregating first-party assemblies whose .Test project wasn't in the collection reads ~0% (unmeasured) and unconditionally blocks termination via the sub-75% branch check; re-scope to instrumented assemblies from the fixed Cobertura
- [durable feature-script triggers Python coverage gate](project_durable-feature-script-triggers-python-coverage-gate.md) — #354: a one-off audit script copied into `<FEATURE>/scripts/*.py` per the durable-script convention is still a committed `.py` file; mandatory coverage gate applies, no exemption for feature-folder tooling
- [SVGControl stale binding redirect out of scope](project_svgcontrol-stale-binding-redirect-out-of-scope.md) — #354: `SVGControl/app.config`'s `System.Runtime.CompilerServices.Unsafe` redirect (6.0.2.0 vs csproj 6.0.3.0) is still stale as of 2026-07-18; check it in any future app.config binding-redirect audit
- [pr-context stale after remediation commit](project_pr-context-stale-after-remediation-commit.md) — #354 R4: pr_context artifacts don't auto-update after a remediation commit lands; compare `git rev-parse HEAD` against the summary's recorded Head ref every re-audit cycle and refresh if stale
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,20 @@ legitimate way to avoid a false trip, but verify this deliberately rather than r

Related: [[coverage-hook-forces-fail-below-floor-despite-exemption]],
[[pr-context-summary-misclassifies-cs]].

**Not C#-only, and not confined to coverage-discussion prose (#354 R4):** the same substring
trap applies to every language's label list (Python labels include the bare word "python";
PowerShell includes "pester"). It also fires from the standard `## Rejected Scope Narrowing`
boilerplate paragraph that every policy-audit now carries: that paragraph routinely says
"...attempted to narrow the audit..., mark any language as **out of scope**, or waive a
toolchain/**coverage** check..." in the same sentence as a language name mentioned elsewhere in
the same line (e.g., "1 refactored **Python** script"). If the language name, the word
"coverage", and a narrowing phrase (`out of scope`, `N/A`, etc.) all land on one physical
line, the hook flags a false "scope narrowing" FAIL even though the paragraph is generic
boilerplate about the *audit process*, not a coverage-row disposition. Fix: keep the Rejected
Scope Narrowing boilerplate sentence free of the changed language's name — move file-count/
language detail into a separate sentence or paragraph with no "coverage"/narrowing wording on
the same line. Always re-run the `Test-LanguageCoverageRow` simulation (or the full
`Invoke-FeatureReviewCoverageValidation` end-to-end with a synthetic `output` payload) against
the *actual drafted policy-audit* for every changed-file language before finalizing, not just
for C#.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: durable-feature-script-triggers-python-coverage-gate
description: A one-off audit/fix script copied into <FEATURE>/scripts/*.py per the atomic-planner's durable-script-copy convention still trips the mandatory per-language Python coverage gate
metadata:
type: project
---

Issue #354 (stale-app-config-binding-redirects): the atomic-planner's `durable-script-copy-into-feature-folder` memory directs copying a proven-correct scratchpad script into `<FEATURE>/scripts/<name>.py` before referencing it in plan tasks, so it survives past the authoring session. That convention is sound for plan durability, but it means the script is a **permanently-committed** `.py` file in the branch diff, not a throwaway. `validate-feature-review-coverage.ps1`'s `Get-ChangedLanguageSet` matches any `.py` bullet line regardless of where it lives (feature folder vs `src/`), so it unconditionally requires a mandatory Python coverage row. In practice no `artifacts/python/lcov.info` exists for a single-script one-off tool, so the correct, honest verdict is **FAIL — coverage artifact absent**, carried into remediation-inputs, even when the script's actual behavior was independently re-verified via a standalone re-run and is functionally correct.

**Why:** The mandatory coverage rule in the feature-review SKILL has no carve-out for "tooling scripts committed to a feature folder rather than production `src/`." Treating it as exempt would be an unauthorized scope-narrowing.

**How to apply:** When a feature-folder diff includes a new `.py`/`.ps1`/`.ts`/`.cs` file under `scripts/` (not just under conventional production paths), still classify it as a changed-file language and require the coverage row. Independently verify the script's *correctness* (re-run it, diff its output, run Black/Ruff/Pyright directly) as strong corroborating evidence for the code-review, but do not let that substitute for the missing coverage artifact in the policy-audit verdict. See [[project_coverage-hook-label-substring-false-positive]] for the adjacent Test-LanguageCoverageRow line-matching mechanics used to write a compliant FAIL row.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: pr-context-stale-after-remediation-commit
description: pr_context.summary.txt/appendix.txt captured during cycle 1 do not auto-update when a remediation commit lands; a re-audit must detect and refresh them before trusting Head ref/coverage-artifact claims
metadata:
type: project
---

Issue #354, remediation_pass 1 re-audit (R4): `artifacts/pr_context.summary.txt` and
`artifacts/pr_context.appendix.txt` were last regenerated during the cycle-1 review and still
recorded `Head ref (resolved): ... @ 96ec70a4...` (the pre-remediation commit) even though the
branch's actual `HEAD` had since advanced to the remediation commit `6c12cfc8` (which added the
pytest suite and refactored the script). The summary's own coverage-artifact claim ("no
`artifacts/python/lcov.info` exists") was also stale by the time of the re-audit.

**Why this matters:** `validate-feature-review-coverage.ps1`'s `Get-ChangedLanguageSet` reads
`artifacts/pr_context.summary.txt`, not `git diff` directly. A stale summary either misses newly
changed languages/files from a later commit, or (worse) still asserts a coverage gap that a
remediation commit already closed, which could mislead a reviewer who trusts the artifact's prose
instead of independently re-running `git rev-parse HEAD` / `git diff --numstat` against the
resolved merge-base.

**How to apply:** On every re-audit cycle (R2+), before writing any review artifact: (1) run
`git rev-parse HEAD` and compare against the summary's recorded `Head ref`; if they differ, the
summary is stale and must be regenerated to cover the full range merge-base..HEAD (not just the
delta since the last cycle); (2) preserve prior-cycle evidence as historical record (do not
delete it) but add a clearly labeled "cycle N" section for the new commit(s); (3) re-verify the
per-language changed-file bullet lines still match the hook's strict `- <path> (+N/-M)` format
exactly (no trailing prose) — see [[project_coverage-hook-label-substring-false-positive]] and
[[project_pr-context-mcp-unavailable-manual-fallback]] for the regex-compliance mechanics.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: svgcontrol-stale-binding-redirect-out-of-scope
description: SVGControl/app.config carries one real, uncorrected stale bindingRedirect (System.Runtime.CompilerServices.Unsafe 6.0.2.0 vs csproj 6.0.3.0) that any future "audit every app.config" AC should treat as a known, pre-existing, out-of-scope condition
metadata:
type: project
---

Discovered during issue #354 (stale-app-config-binding-redirects) review, 2026-07-18: a standalone comparator cross-checking every project's `.csproj` `<Reference Version=...>` against its `app.config` `<bindingRedirect>` found exactly one real mismatch outside that issue's 57-item fix inventory — `SVGControl/app.config`'s `System.Runtime.CompilerServices.Unsafe` redirect caps at `newVersion="6.0.2.0"` while `SVGControl.csproj` references `Version=6.0.3.0` (package `System.Runtime.CompilerServices.Unsafe.6.1.2`).

**Why this matters:** `SVGControl`/`SVGControl.Test` are already established (per [[project_csharp-repowide-coverage-below-80]] and cross-session `csharp-analyzer-packages-config-quirks.md`/`project_repo_sdk_and_nullable_rebuild.md` memory in other agents' memory stores) as vendored/exempt from this repo's analyzer and nullable build gates. Issue #354's own fix script hardcodes `EXCLUDE_PROJECTS = {"SVGControl", "SVGControl.Test"}` and `issue.md`'s Suspected-Cause project inventory never names them either — consistent, but AC1's literal text ("every first-party project's app.config") does not itself state this carve-out, so a future strict re-audit of AC1 will surface this same gap again unless it is fixed or the AC wording is formally narrowed.

**How to apply:** If a future feature/bug touches `app.config` binding redirects again (broad audit, dependency bump remediation, etc.), check `SVGControl/app.config`'s `System.Runtime.CompilerServices.Unsafe` entry specifically — it was still stale as of 2026-07-18. Don't assume a clean audit script run against the non-vendored project set means the repo has zero remaining stale redirects.
10 changes: 5 additions & 5 deletions QuickFiler.Test/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
publicKeyToken="31bf3856ad364e35"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-8.18.0.0" newVersion="8.18.0.0" />
<bindingRedirect oldVersion="0.0.0.0-8.19.2.0" newVersion="8.19.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -176,7 +176,7 @@
publicKeyToken="92742159e12e44c8"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-1.11.0.0" newVersion="1.11.0.0" />
<bindingRedirect oldVersion="0.0.0.0-1.14.0.0" newVersion="1.14.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -296,7 +296,7 @@
publicKeyToken="31bf3856ad364e35"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.75" newVersion="3.1.1.75" />
<bindingRedirect oldVersion="0.0.0.0-3.1.2.115" newVersion="3.1.2.115" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -464,7 +464,7 @@
publicKeyToken="0a613f4dd989e8ae"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-4.84.0.0" newVersion="4.84.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.86.1.0" newVersion="4.86.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand All @@ -488,7 +488,7 @@
publicKeyToken="0a613f4dd989e8ae"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-4.84.0.0" newVersion="4.84.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.86.1.0" newVersion="4.86.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down
10 changes: 5 additions & 5 deletions Tags.Test/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
publicKeyToken="31bf3856ad364e35"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.75" newVersion="3.1.1.75" />
<bindingRedirect oldVersion="0.0.0.0-3.1.2.115" newVersion="3.1.2.115" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -324,7 +324,7 @@
publicKeyToken="31bf3856ad364e35"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-8.18.0.0" newVersion="8.18.0.0" />
<bindingRedirect oldVersion="0.0.0.0-8.19.2.0" newVersion="8.19.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -384,7 +384,7 @@
publicKeyToken="0a613f4dd989e8ae"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-4.84.0.0" newVersion="4.84.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.86.1.0" newVersion="4.86.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand All @@ -408,7 +408,7 @@
publicKeyToken="92742159e12e44c8"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-1.11.0.0" newVersion="1.11.0.0" />
<bindingRedirect oldVersion="0.0.0.0-1.14.0.0" newVersion="1.14.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Azure.Core" publicKeyToken="92742159e12e44c8" culture="neutral" />
Expand Down Expand Up @@ -444,7 +444,7 @@
publicKeyToken="0a613f4dd989e8ae"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-4.84.0.0" newVersion="4.84.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.86.1.0" newVersion="4.86.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down
10 changes: 5 additions & 5 deletions TaskMaster.Test/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
publicKeyToken="31bf3856ad364e35"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-8.18.0.0" newVersion="8.18.0.0" />
<bindingRedirect oldVersion="0.0.0.0-8.19.2.0" newVersion="8.19.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -172,7 +172,7 @@
publicKeyToken="92742159e12e44c8"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-1.11.0.0" newVersion="1.11.0.0" />
<bindingRedirect oldVersion="0.0.0.0-1.14.0.0" newVersion="1.14.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -292,7 +292,7 @@
publicKeyToken="31bf3856ad364e35"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.75" newVersion="3.1.1.75" />
<bindingRedirect oldVersion="0.0.0.0-3.1.2.115" newVersion="3.1.2.115" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down Expand Up @@ -460,7 +460,7 @@
publicKeyToken="0a613f4dd989e8ae"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-4.84.0.0" newVersion="4.84.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.86.1.0" newVersion="4.86.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand All @@ -484,7 +484,7 @@
publicKeyToken="0a613f4dd989e8ae"
culture="neutral"
/>
<bindingRedirect oldVersion="0.0.0.0-4.84.0.0" newVersion="4.84.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.86.1.0" newVersion="4.86.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity
Expand Down
Loading
Loading