Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b8776b5
docs(614): promote EFC store-root path-leak bug to active feature folder
drmoisan Aug 26, 2026
34350f4
docs(615): record off-chain analyzer version skew found during 614 bo…
drmoisan Aug 26, 2026
a8a9656
docs(614): add store-root path-leak defect census research
drmoisan Aug 26, 2026
aec3f18
docs(614): promote three off-chain findings from the defect census
drmoisan Aug 26, 2026
5a42948
chore(git): ignore per-session .claude/state agent budget files
drmoisan Aug 26, 2026
0a41161
docs(614): author full-bug spec from the confirmed defect census
drmoisan Aug 26, 2026
d032323
docs(614): author validated 11-phase atomic plan for the defect chain
drmoisan Aug 26, 2026
ca7f686
docs(614): repair six unfailable plan gates found by executor preflight
drmoisan Aug 26, 2026
75b728f
docs(614): allowlist pre-plan branch paths and scope the redaction sweep
drmoisan Aug 26, 2026
f602410
chore(614): record preflight lessons in agent memory
drmoisan Aug 26, 2026
ebbfb40
test(614): add failing store-root regression test with fail-before ev…
drmoisan Aug 26, 2026
1470f96
docs(614): correct a test that codifies the D1 defect as expected beh…
drmoisan Aug 26, 2026
33bcd21
fix(614): route breadcrumb router selection through the archive stem …
drmoisan Aug 26, 2026
cee7897
fix(614): enforce the archive stem contract at the filing boundary an…
drmoisan Aug 26, 2026
519ca59
fix(614): derive filing stems through the contract and fail fast in A…
drmoisan Aug 26, 2026
f67fb6f
test(614): cover the separator-only root branch and de-flake the diag…
drmoisan Aug 26, 2026
ff04bf0
docs(614): check off all 26 acceptance criteria and record the comple…
drmoisan Aug 26, 2026
1cb8ee3
docs(614): record the final clean-tree verification artifact
drmoisan Aug 26, 2026
437c8e5
docs(614): mark P10-T28 complete in the plan checklist
drmoisan Aug 26, 2026
7943aed
chore(614): record executor lessons on backslash collapse and shared …
drmoisan Aug 26, 2026
0209250
docs(614): promote two pre-existing defects found during execution
drmoisan Aug 26, 2026
0661c9f
docs(614): add feature review artifacts
drmoisan Aug 26, 2026
6bbb18e
docs(614): open remediation cycle 1 for two introduced regressions
drmoisan Aug 26, 2026
0fb0efe
docs(614): add remediation cycle 1 plan for CR-1 and CR-2
drmoisan Aug 26, 2026
cbad2da
fix(quickfiler): remediate #614 review findings CR-1/CR-2 (filing gua…
drmoisan Aug 26, 2026
b45e2a2
docs(efc): record the #614 remediation cycle 1 commit gate and close …
drmoisan Aug 26, 2026
5de3c26
docs(614): record cycle 1 re-audit NO-GO and open remediation cycle 2
drmoisan Aug 26, 2026
0805766
docs(614): re-scope remediation cycle 2 to a partial revert and open …
drmoisan Aug 26, 2026
6c5aba3
docs(614): add remediation cycle 2 plan, a partial revert of the CR-2…
drmoisan Aug 26, 2026
7abdebf
docs(614): correct two blocking preflight defects in the cycle-2 plan
drmoisan Aug 26, 2026
048cc87
docs(614): record the D6 reachability assessment and open #638
drmoisan Aug 26, 2026
8377616
docs(614): keep the pre-cycle-1 rejection test the revert would have …
drmoisan Aug 26, 2026
a3b264f
docs(614): stop P4-T1 launching live Outlook, and make the retained-l…
drmoisan Aug 26, 2026
01e26f7
docs(614): anchor the retained-line gate on the deterministic coverag…
drmoisan Aug 27, 2026
98b7a5e
fix(quickfiler): reject rooted selections at filing boundary
drmoisan Aug 27, 2026
8188cff
docs(614): record remediation cycle 2 completion
drmoisan Aug 27, 2026
e8d8f52
docs(614): record accepted-risk review disposition
drmoisan Aug 27, 2026
eaf29fb
fix(app-globals): inject OneDrive environment reader for tests
drmoisan Aug 27, 2026
e33ec43
docs(review): record Issue 614 cycle 3 audit results
drmoisan Aug 27, 2026
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
175 changes: 90 additions & 85 deletions .claude/agent-memory/atomic-executor/MEMORY.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: csharpier-chain-wrap-defeats-singleline-search-gates
description: CSharpier wraps fluent/chained C# call expressions across lines, so a plan's zero-hit gate on a literal like `fsPath.Substring(3)` returns 0 hits BEFORE any work and gates nothing; always grep the exact literal during preflight
metadata:
type: project
---

A plan's "fixed-string search returns zero hits after the fix (present pre-change, so the
gate can fail)" clause is only true if the literal occupies ONE line in the CSharpier-formatted
source. CSharpier breaks a chained call over multiple lines once the chain exceeds the print
width, so a plausible-looking literal composed from a receiver plus a chained member is
frequently absent from every single line.

Verified 2026-08-26 during #614 preflight, `UtilitiesCS/OutlookObjects/Folder/FolderConverter.cs`:

```
157 var fsPathExDividers = fsPath
158 .Substring(3)
159 .Replace($"{Path.DirectorySeparatorChar}", "");
```

`grep -Fc 'fsPath.Substring(3)'` returns **0**. The plan asserted this literal was "present
pre-change", so its zero-hit gate passed before the executor touched anything. This is exactly
the G6 case in `.claude/rules/plan-acceptance-gates.md` (a literal present only across a
line wrap), but the validator reports it only as a Warning, so it can reach an executor.

**Why:** the plan author reads a construct in a rendered file or from a research snapshot that
quotes the expression logically (`fsPath.Substring(3)`), not as the formatter emitted it.

**How to apply:** during preflight, run `grep -Fc '<literal>' <file>` for EVERY search-based
acceptance condition — zero-hit gates must return >= 1, at-least-one-hit gates must return 0.
Do not accept the plan's own "present pre-change" parenthetical as evidence. When a gate is
vacuous, the fix is either a single-line literal from the same construct (`.Substring(3)` on
its own line, or the full assignment line) or, preferably, a named test whose node ID is
stable under reformatting.

Related: [[feedback-verify-line-citations-with-numbered-output]],
[[csharpier-formats-xml-print-width]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: log4net-memoryappender-shared-per-type-across-parallel-classes
description: log4net binds one logger per TYPE, so a MemoryAppender attached in one test class captures events from concurrently-running tests in other classes; exact-count assertions on it are order-dependent
metadata:
type: project
---

`log4net.LogManager.GetLogger(typeof(X))` returns ONE logger per type for the whole process. A
`MemoryAppender` attached to it in `[TestInitialize]` therefore captures events emitted by every
test that drives `X`, including tests in OTHER classes running in parallel (MSTest here runs at
`Workers: 24, Scope: ClassLevel`). `[TestCleanup]` detaching does not help: the contamination
happens during the test, not after it.

**Why:** `SegmentActivate_CrossStoreAncestor_LeavesSelectionUnchangedAndDiagnoses` asserted
`RenderedMessages().Should().ContainSingle(m => m.Contains("rejected"))` and failed intermittently
with "but 2 such items were found". It passed on the first full-suite run and failed on the next
with no code change between them. The extra event came from another router test class
(`BreadcrumbBridgeRouterIssue439Tests`, `BreadcrumbBridgeRouterTests`) emitting its own rejection
diagnostic in the same window. `[DoNotParallelize]` on the asserting class alone would not fix it —
every writer class would have to be marked too, and a future writer class would silently reintroduce
the flake.

**How to apply:** when a spec requires proving "a diagnostic is emitted", assert EXISTENCE, never a
count:

```csharp
messages.Should().Contain(m => m.Contains(fragment));
messages.Where(m => m.Contains(fragment))
.Should().OnlyContain(m => !m.Contains("@"));
```

Concurrency can only ADD events, never remove them, so the existence claim is deterministic. Pair it
with a behavioural assertion in the same test (here: the selection is unchanged) — that is what
proves the diagnostic came from THIS instance, since had it not rejected, the selection would have
changed. Also scope any "message must not leak X" assertion to the matching subset: an unfiltered
`NotContain` can fail on a concurrent test's unrelated log line that legitimately contains a path.

Note this also means `QuickFiler.Test` needed a log4net `<Reference>` plus a `packages.config` pin
added before the appender pattern (established in `TaskMaster.Test/AppGlobals/AppEventsTests.Helpers.cs`)
could be used at all — non-SDK `ProjectReference` does not flow package references to the compiler.
See [[legacy-csproj-no-transitive-compile-refs]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: preflight-gate-literal-extract-from-plan-not-retype
description: Never re-type a plan's fixed-string gate literal into a shell to verify it; extract it from the plan file programmatically, or quoting bugs manufacture false zero-hit blockers
metadata:
type: feedback
---

When preflight-validating a "fixed-string search returns zero/N hits" gate, extract the literal
from the plan file with a regex over its backtick spans and feed THAT string to the search. Do not
re-type the literal into a `pwsh -Command` or `bash printf` invocation.

**Why:** On the #614 preflight (2026-08-26) I re-typed the P3-T2 literal
`return root + "\\" + presentedTarget.TrimStart('\', '/');` into a PowerShell double-quoted
string. Inside `"..."` PowerShell does NOT treat `''` as an escaped quote (that is single-quote-string
syntax only), so the pattern was silently corrupted and `Select-String -SimpleMatch` returned 0 hits.
The plan was correct — the literal is on source line 162 verbatim — but a 0-hit result on a
"must be >=1 now" gate reads exactly like a genuine unfailable-gate blocker. Re-typing through a
bash heredoc into `printf` corrupted the same string a second, different way. Two independent
quoting layers (bash -> pwsh -> regex/SimpleMatch) each eat backslashes and quotes differently, and
the corruption is invisible unless you echo the constructed pattern.

**How to apply:** For every gate literal, run a script that does
`[regex]::Matches($planLine, '`([^`]+)`')`, picks the span by a short unambiguous prefix, prints the
extracted literal AND its `.Length`, then searches the target file with it. Also print the target
source line trimmed and assert `-ceq`. If a gate ever reads 0 hits when the plan says it should
match, print the pattern before reporting it — assume your own quoting first, the plan second.
Related: [[verify-line-citations-with-numbered-output]],
[[csharpier-chain-wrap-defeats-singleline-search-gates]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: pwsh-file-array-param-from-bash
description: "`pwsh -NoProfile -File script.ps1 -Tokens a,b,c` binds ONE string, not a string[] - a gate-literal counter then silently reports HITS=0 for every token; drive multi-value input from a TSV file instead"
metadata:
type: project
---

`pwsh -NoProfile -File <script>.ps1` passes every argument as a plain string. A `[string[]]$Tokens`
parameter given `a,b,c` from Bash binds a SINGLE element whose text is `a,b,c`.

**Why:** on the #614 remediation cycle a literal-gate counter was invoked as
`-Tokens 'MinimumCreationLength','ResolveArchiveRootOrEmpty',...`. It printed one line,
`0 MinimumCreationLength,ResolveArchiveRootOrEmpty,RootUnavailableDiagnostic,...`. Read carelessly
that is five zero-hit gates on work that was already done — a false blocker on a correct edit. The
failure is silent: no binding error, no warning, just a nonsense count for a token that does not
exist.

**How to apply:** for any script that takes a list (gate literals, file paths, test names), give it
ONE `-TokenFile` parameter and write the pairs to a tab-separated scratchpad file with
`printf '%s\t%s\n'`. Read it with `Get-Content` and `.Split([char]9)`. This also composes with
[[preflight-gate-literal-extract-from-plan-not-retype]]: the same TSV can be produced by parsing the
plan's backtick spans rather than re-typing them.

Count occurrences with `$txt.IndexOf($t, $i, [System.StringComparison]::Ordinal)` in a loop, not
with `grep`, so the count is an ordinal occurrence count immune to shell quoting and to
[[tool-layer-collapses-double-backslash-in-file-content]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
name: tool-layer-collapses-double-backslash-in-file-content
description: Bash/Write tool content silently collapses `\\` to `\`, corrupting C# verbatim path literals, PowerShell regexes and evidence quotes; author such files via a Python generator using a sentinel
metadata:
type: project
---

Any file content passed through the Bash tool's `command` parameter (heredocs included) or the
Write tool has its `\\` sequences collapsed to a single `\`. A single `\` survives; `\"` survives.
The collapse is silent — nothing errors, the file just contains the wrong text.

**Why:** observed three times in one #614 run, each with a different failure mode:

1. A C# test literal `@"\\mailbox@example.com"` was written as `@"\mailbox@example.com"`. The test
still failed pre-fix (a single leading `\` is also a full Outlook path), so the `[expect-fail]`
gate passed and the corruption nearly shipped. Only re-reading the written file caught it.
2. `private const char BackslashSeparator = '\\';` was written as `'\';` → CS1010 "Newline in
constant". This one fails loudly.
3. A PowerShell regex `"\\(obj|bin|...)\\\\"` became `"\(obj|bin|...)\\"` → "Invalid pattern ...
Too many )'s", which aborted a whole QC-loop script mid-run.

Evidence artifacts are also affected: a quoted FluentAssertions message
`not to be "\\mailbox@example.com"` landed in a committed `.md` as `"\mailbox@example.com"`,
misquoting the recorded failure output.

**How to apply:** when creating or editing ANY file whose content contains backslashes — C# verbatim
path literals, `char` escapes, regex patterns, Windows paths in markdown — do NOT write it directly.
Write a Python generator script (Write tool) that builds the text with a `BS = chr(92)` sentinel:

```python
BS = chr(92)
body = body.replace("BSBS", BS + BS).replace("BS", BS)
```

Order matters: replace the two-char sentinel first. Then run the generator with Bash and grep the
result to confirm the backslashes are right before building. The same sentinel trick is needed for
`.ps1` scripts you generate, which is more reliable than any level of inline `pwsh -Command`
quoting.

**Simpler variant, confirmed working on the #614 remediation cycle (2026-08-26):** author the file
with the Write tool using a literal placeholder such as `@@BS@@` for EVERY backslash (so the written
content contains zero backslashes and there is nothing to collapse), then run a tiny reusable
`.ps1` in the scratchpad that does `$txt.Replace('@@BS@@', [string][char]92)` and rewrites the file
with a BOM-less `UTF8Encoding($false)`. It reported the replacement count each time (20, 2, 6, 13),
which doubles as a check that no sentinel was missed. This survives repeated `Edit` calls on the
same file — edit with sentinels, re-run the desentinel script, done — and needs no Python.

The same hazard bit a large Markdown append: a `cat > file <<'EOF'` heredoc carrying ~110 lines of
prose died with ``unexpected EOF while looking for matching `'``. Write the body with the Write tool
to the scratchpad and `cat` it onto the target instead of embedding prose in a heredoc.

Related: [[preflight-gate-literal-extract-from-plan-not-retype]] covers the read side (extract gate
literals programmatically); this note covers the write side. Verify with Python `str.count()` on a
fixed string rather than `grep -F`, which returned 0 for a literal Python counted as 1.
4 changes: 4 additions & 0 deletions .claude/agent-memory/atomic-planner/MEMORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Atomic Planner Memory Index

- [Deletion-adjusted coverage no-regression gate](deletion-adjusted-coverage-no-regression-gate.md) — deleting covered lines makes `rate_post >= rate_base` unsatisfiable; gate on covered/valid counters

- [Verify test provenance before planning a deletion](verify-test-provenance-before-planning-deletion.md) — in a revert plan, read the test at the pre-cycle commit; a two-arg call shape doesn't prove the cycle added it
- [#614 store-root leak plan seams](project_614_store_root_leak_plan_seams.md) — AC25 net non-growth (3 over-limit files); E1 SelectRow out-of-root-only pinning; remediation C1: behavior-preserving seam phase reconciles fail-before with a signature change, resolver-in-guard beats inline try/catch on line budget + coverage, net48 IsNullOrWhiteSpace doesn't narrow (`archiveRoot!`)
- [Agent worktrees need SDK + NuGet + analyzer-backfill bootstrap](agent-worktrees-need-sdk-and-nuget-bootstrap.md) — no `.dotnet-sdk`, no `packages/`, and a clean restore still misses the skewed analyzer versions (CS0006, not a warning); three Phase 0 tasks
- [/Logger:trx needs /ResultsDirectory](trx-needs-resultsdirectory.md) — TRX lands in `TestResults\` relative to cwd; TRX-existence-under-evidence acceptance is unsatisfiable without it, and the clean-tree gate won't catch it
- [Per-task TRX subdirectory](trx-needs-resultsdirectory.md) — a shared `/ResultsDirectory:` makes "ten distinct TRX files" ambiguous once `[expect-fail]` runs deposit earlier TRX there; give each task a `p#-t#` segment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: deletion-adjusted-coverage-no-regression-gate
description: "Reverts/removals that delete fully-covered lines make a raw-rate `post >= baseline` coverage gate arithmetically unsatisfiable; gate on covered/valid counters instead"
metadata:
type: project
---

When a plan cycle deletes fully-covered production lines (a revert, dead-code removal, or helper deletion), a raw-rate no-regression gate `line-rate_post >= line-rate_base` is unsatisfiable by arithmetic: removing 100%-covered lines from a pool below 100% lowers the aggregate rate even when every surviving line keeps its coverage. A one-re-run allowance does not help — the miss is deterministic, not measurement noise. This is the mirror image of a gate that cannot fail: both fail to measure what they claim (#614 cycle-2 B-2; miss was -0.0020 pp).

Correct gate form, per counter (lines and branches separately), read from the Cobertura roots before and after:

- `valid_post <= valid_base` (the change only removes from the denominator), AND
- `covered_post >= covered_base - (valid_base - valid_post)` (every removed line/branch was covered; no retained one regressed).

Report the raw rates informationally with the arithmetic tying any decrease to the deleted covered lines; a raw rise also satisfies the gate. Keep the single re-run allowance only for a counter-gate miss (denominator nondeterminism).

**When one deleted line was UNCOVERED the line gate carries that many lines of slack**, so it no longer proves "no retained line lost coverage". Close the slack with a per-`filename` comparison, never with the changed-line listing: a retained line is by definition not a changed line, so a retained-line check performed against a changed-line listing returns clean whichever retained line regressed (#614 cycle-2 R3 F5-2 — the R2 edit that prescribed exactly that was itself unfalsifiable). Correct form: define `D_covered` as the NET sum of (`lines-covered` base minus post) over just the EDITED files, read from the two Cobertura files (never by hand-counting deleted source lines — a revert that replaces a body also ADDS measured lines, so a hand count breaks the identity on correct work), then assert `lines-covered_base - lines-covered_post == D_covered`. Two carve-outs are mandatory or the gate fails on correct work: (1) a strict excess means retained lines GAINED coverage (a new test in the same cycle commonly does this) and must pass; (2) an equal-and-opposite gain can mask a single-line regression, so additionally require the signed per-`filename` `lines-covered` delta for every retained file that moved, and fail on any negative delta.

**Why:** #614 remediation cycle 2 preflight verified the projected raw miss independently and returned REVISIONS REQUIRED; the branch-rate comparison was within the observed ±0.002 pp run-to-run spread, i.e. a coin flip.
**How to apply:** whenever a plan's change set deletes covered production lines, never write `rate_post >= rate_base` as a gate. Pre-compute the projected counters in the plan so the executor can check satisfiability. Related: [[project-deadcode-removal-vs-coverage-exclusion]], [[project-614-store-root-leak-plan-seams]].
Loading
Loading