fix(quickfiler): rename the EFC banner-rejection prefix and correct the stale SelectedFolder comment (#662) - #721
Merged
drmoisan merged 25 commits intoSep 1, 2026
Conversation
Create the active feature folder for issue 662 and author issue.md with the content the bug issue template does not emit: an explicit Acceptance Criteria section, the directional constraint on the remedy, and the derived occurrence baseline. The issue's Expected Behavior asks for one banner-prefix arity across producers and classifiers. Unifying upward is a behavioural relaxation: EfcSelectionGuard's three-character prefix is the only mechanism that rejects a three-equals row at either EFC classification site, because IsBannerRow classifies by the producers' four-character constant. The merged test IsSelectableFolder_AndIsBannerRow_ClassifyThreeAndFourEquals RowsIdentically asserts both rows are rejected on both paths. The remedy therefore preserves the guard's breadth, renames its constant, corrects the stale comment, and dedupes the producers' literal. Occurrence counts in the acceptance criteria are scoped to a '*.cs' pathspec. An unscoped search for the anchored four-character declaration also matches two historical audit records under a closed feature folder, so the unscoped count cannot reach the asserted value. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Record the research findings for issue 662. All eight orchestrator hypotheses were independently confirmed, including the directional constraint: widening EfcSelectionGuard's prefix to four characters makes IsValidCreationSelection and IsValidFilingSelection accept a three-equals row, and EfcFormControllerTests.cs:463 then fails. Research surfaced two defects in the acceptance criteria as first written, both corrected here. AC2 and AC5 were mutually unsatisfiable. AC5 as written allowed FolderSuggestionTree to keep an aliasing constant, but such a line still matches AC2's declaration regex, so AC2's asserted count of one would have been two. AC5 now requires deletion of the constant with a direct qualified reference from its single reader. AC2's second verification counted every occurrence of the new name in the guard file, which AC3's doc-comment wording could perturb. It now counts the two StartsWith call sites only. Also: the unscoped occurrence figure is scope-dependent and grows as this feature's own documents are authored, so no absolute unscoped count is asserted. Diff anchors moved from origin/main to the pinned base commit so the assertions cannot drift when main advances. AC5b added to record that BreadcrumbRowBuilder.cs is read-only to this work, because feature 498's criteria assert that file is unmodified. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Three phases, 52 tasks. Replaces the generic seven-phase scaffold, which used bold phase text rather than the contract's heading form and cited Python test paths. Records the directional constraint as decision D1: FolderSuggestionTree's constant is deleted rather than re-aliased, because an aliasing declaration still matches AC2's declaration regex and would make that count two instead of one. Not yet validated or preflighted. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Nine blocking and six non-blocking defects, enumerated in one review pass with a complete per-task delta. Signal was REVISIONS REQUIRED with CONVERGENCE: NO FURTHER ROUNDS EXPECTED. The three most consequential: the msbuild and vstest variables are resolved in a prose block but consumed in per-task command spans, and shell state does not persist between tasks; the coverage-enabled test runs name the scripts/vscode CLI runsettings, which carries MSTest parallelization only and no coverage collector, so they would instrument the Deedle and FSharp modules the repository maintains two exclusion lists to keep out; and P1-T1 mandated a delegation the executor has no tool to perform. Recorded before the plan revision so the defects found before execution are distinguishable from any found during it. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Applies all 20 delta items plus the two tasks B1 named but the per-task list omitted (P0-T8, P0-T9 also consume the msbuild variable). The planner rejected five delta items as written and corrected them: - P2-T10 gave pre-change line numbers labelled as post-format. All three shift: the new multi-line XML doc sits above the guard call sites, the deleted declaration sits above the FolderSuggestionTree reader, and CSharpier wraps the 132-character rewritten reader. Replaced with an enclosing-member identification rule that records the resolved numbers. - P2-T23's '*.xml' pathspec matches the plan's own committed Cobertura evidence, so the union could never equal the four in-scope files. The feature folder is now excluded from both pathspecs. - Three 'replace the first sentence' items subsumed the sentence after them, which would have left duplicated or contradictory text. - Bringing the test steps inside the Phase 2 restart loop left the loop untriggerable, because the test tasks' acceptance always holds. Each now restarts only when its failed count exceeds the Phase 0 baseline for the same assembly. Still 3 phases and 52 tasks; no task added, removed, or renumbered. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…sue 662 Includes a size-hook-required compaction of the shared MEMORY.md index. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
All 15 round-1 defects confirmed closed. Four new blocking and six non-blocking defects found in one pass covering all 52 tasks. D1 is the consequential one: the six vstest runs each write a TRX, and .gitignore covers *.coverage and *.coveragexml but not *.trx. A TRX carries the account and machine identifiers in runUser, computerName and runDeploymentRoot, and the worktree root in the storage attribute of every unit-test element. The two Cobertura copies carry an absolute filename per class node whenever the document is the raw pre-processed one, which the plan treats as an ordinary state. The git add spans would have staged all of it. D2 through D4 concern evidence that cannot fail: a loop restart can leave the previous pass's TRX in place for the reading tasks to report as the final pass's; the class-node comparison is undefined when a filename carries more than one node, leaving the executor to choose the evidence it is judged against; and the changed-code line rule resolves one of the three statements to the minimum-length comparison rather than to the renamed call site. Convergence is FURTHER ROUNDS LIKELY, so a confirming round follows. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
All ten items D1 through D10 applied. Every underlying delta fact was re-verified against the tree and none was wrong; .gitignore does carry *.coverage and *.coveragexml and does not carry *.trx, which is D1's premise. Two supplied items were defective as written and were applied with a reported correction rather than silently rewritten: - D1's verification search covered the whole feature folder, but the plan file itself legitimately carries the worktree root in its Working Directory section, so the gate could never pass. The plan file is now excluded from the search and the rewrite scope is filtered by git check-ignore so the sweep cannot corrupt the binary .coverage file that /EnableCodeCoverage writes into the results directories. - D10 recorded the agent-memory status into P2-T23's own artifact after the main commit, but that artifact sits inside the pathspec of the same task's 'returns no output' status gate, so the acceptance could never hold. An ordering clause now stages it correctly. The planner also harmonized two sentences in P2-T10 that sat outside the replaced span and still used the single-line-number convention D4 exists to remove. Still 3 phases and 52 tasks; no task added, removed, or renumbered. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
The round-2 delta added an artifact-hygiene rule forbidding an absolute host path, an account name, or a machine name in any file this plan commits. The plan's own Working Directory section carried the absolute worktree root, including the account name, so the plan contradicted the rule it now enforces and the rule's verification search could not pass over the plan file. The path is replaced with a reference to the worktree root the delegating orchestrator supplies. The executor takes its working directory from the delegation prompt, not from this file, so nothing load-bearing is lost. Twenty-six other committed plans in this repository carry the same absolute path, so this is a pre-existing repository-wide condition rather than something introduced here. Only this plan is changed; the others are out of scope for issue 662. The account name now appears nowhere in the feature folder. Structure is unchanged at 3 phases and 52 tasks, line endings remain LF, and the plan validator passes. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
PREFLIGHT: ALL CLEAR with CONVERGENCE: NO FURTHER ROUNDS EXPECTED, over all 52 tasks and every prose region, with every citation and count re-derived against the tree. All ten round-2 defects closed and all four planner deviations accepted. The reviewer confirmed the two gates that matter most are falsifiable rather than pre-satisfied: the AC5b diff currently prints nothing at exit 0 and the P2-T23 scope diff is currently empty, so both can still fail. Seven non-blocking observations recorded rather than remediated. The notable one is that the plan-file carve-out D1 introduced is now dead text, because the absolute host path it was written to accommodate was removed from the plan afterwards. Excluding a file that cannot match changes no outcome, so it was left rather than spending a fourth round. Three rounds against a two-round target. The overrun cause on record is that the planner substituted its own wording for supplied delta items, leaving that text unreviewed until the next round; supplying verbatim replacement text and requiring an explicit per-item disposition is what closed the loop. Refs #662 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Two reusable findings. First, .gitignore covers *.coverage and *.coveragexml but not *.trx, so a committed TRX leaks the account name, machine name and worktree root, the last once per test in the storage attribute. Second, preflight round overrun on this item came from the planner paraphrasing supplied delta items rather than from the review; supplying verbatim replacement text and demanding a per-item disposition is what converged it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…d-stale-comment-662
…-banner-prefix-arity-and-stale-comment-662 # Conflicts: # .claude/agent-memory/atomic-planner/MEMORY.md # .claude/agent-memory/task-researcher/MEMORY.md
The plan pinned 2b85134 as the diff anchor for P2-T16, P2-T18 and P2-T23. That commit is an ancestor of both this branch and origin/main, so the two-dot diff form those gates use reports origin/main's own accumulated changes alongside this branch's work. Measured before any plan task ran and before any edit, P2-T23's listing returned 22 paths against an asserted union of 4. Replace the anchor for those three tasks with the merge base of origin/main and HEAD, resolved at run time. The same listing returns empty pre-edit under the corrected anchor. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Captures the Phase 0 baseline for issue 662: policy reads, base-commit resolution with the execution amendment, SDK/tool/package bootstrap, read-only CSharpier baselines, the two msbuild gate baselines, both full-assembly test baselines, the numeric coverage baseline, and the pre-change occurrence counts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…stale comment Phase 1 of issue 662. The guard's constant keeps the value "===" exactly; only its name and its XML documentation change. - EfcSelectionGuard: rename BannerPrefix to BannerRejectionPrefix, update both StartsWith call sites, and rewrite the XML doc to record that the value is deliberately a proper prefix of BreadcrumbRowBuilder.BannerPrefix, that it therefore rejects a strict superset of the producers' banner rows, and that it must not be widened. - EfcFormController: replace the SelectedFolder comment, which claimed IsValidSelection keeps a four-character rejection, with the composition the code implements. Comment only. - FolderSuggestionTree: delete the duplicated four-character declaration and read BreadcrumbRowBuilder.BannerPrefix directly, so the two producers share one declaration. Deletion rather than aliasing, per Decisions Record D1. - EfcSelectionGuardTests: add one regression test pinning that both arities are rejected on both predicates, with a because message naming the prohibited direction. Behaviour-preserving at every call site; the fail-before exception dossier records why no failing run exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Records the final clean Phase 2 loop pass: CSharpier format (no rewrite) and check, both msbuild gates, the two scoped AC6/AC7 runs, and both full-assembly runs. Includes the CSharpier wrap of the FolderSuggestionTree IsBanner reader, which P1-T6 predicted and which triggered the single loop restart. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
… evidence Completes Phase 2 for issue 662. All ten acceptance criteria in issue.md are verified and checked off, each with its own verification artifact. - Per-criterion verification artifacts for AC1 through AC9. - Post-change coverage capture and the baseline-vs-post-change delta: changed-code coverage 3/3, and neither touched file's class line-rate falls. - Issue acceptance-criteria mirror and the AC status summary. - Scope confirmation: the union of the anchored diff and the porcelain status is exactly the four in-scope files. AC5b and AC7 were verified against the merge base of origin/main and HEAD, per the plan's Execution Amendment, because the plan's pinned anchor is an ancestor of both refs and its two-dot diff would have reported unrelated origin/main work. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Adds two atomic-executor memories from the issue 662 run: - a full-assembly test run can hang although the byte-identical Phase 0 baseline command passed; sample testhost CPU to distinguish hang from slow, and diff the failing set against the baseline before calling it a regression. - never write an observed value into an evidence artifact before observing it. Compacts the memory index from 161 to 102 lines by pairing related entries onto single lines, as a repository hook requires it below its read limit. Every pointer is preserved. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
The artifact-hygiene sweep substitutes angle-bracket placeholder tokens, and vstest writes the redacted values into XML attribute values in the TestRun header. A raw angle bracket is not permitted there, so all six committed TRX files were left not well-formed and failed to parse at line 2. Escape the placeholder tokens inside the TRX files only. The parsed attribute value is still exactly the placeholder, so the redaction is unchanged and the sweep's zero-residual result still holds, while the documents parse again. The hygiene gate did not catch this because it asserts only that the identifiers were removed, never that the rewritten file is still parseable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Policy audit, code review and feature audit for the branch. Zero blocking findings; all ten acceptance criteria re-derived and passing. One coverage row is recorded FAIL rather than PASS: EfcFormController.cs sits at 25.5 percent line coverage against the uniform per-file floor. It is dispositioned non-blocking because the file's diff contains no executable line, its coverage counters are byte-identical to baseline, and it falls in the ratified Outlook Interop exemption class. Recording it as passing would have been inaccurate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Adds the sibling-assertion blind spot found on this branch: the assertion that reads like the consistency guard still passes under the prohibited widening edit, and only the BeFalse line catches it. Extends the shared host-path note with the XML-escaping caveat, since a zero-residual sweep result is not sufficient evidence that the rewritten artifact is still parseable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves the banner-prefix arity divergence and the stale
SelectedFoldercomment described in issue #662.Three
BannerPrefixconstants existed under one name carrying two different values:EfcSelectionGuarddeclared a three-character"===", while both row producers (BreadcrumbRowBuilderandFolderSuggestionTree) declared a four-character"====". After this change there are two declarations rather than three: one producer constant shared by both producers, and one deliberately broader classifier constant that is renamed, documented, and test-pinned as broader.This is a consistency and comment-accuracy fix. It is explicitly not a behavioural repair, and no predicate's return value changes for any input.
The direction this fix deliberately does not take
The issue reads as though the two arities should be unified upward to four characters. That reading is wrong and would be a behavioural regression, so it is worth stating plainly for reviewers:
EfcFormController.IsBannerRowclassifies by the producers' four-character constant, soIsBannerRow("===")isfalse.IsValidCreationSelectiontestsvalue.Length >= MinimumCreationLength, andMinimumCreationLengthis 3, so for the input"==="that comparison is3 >= 3and the length rule rejects nothing.Widening the guard to
"===="would makeIsValidFilingSelection("===")andIsSelectableFolder("===")both returntrue. The guard's value is unchanged; only its name and its documentation change.One detail is worth flagging because it is the kind of thing a future reviewer would get wrong. In the pre-existing merged test
IsSelectableFolder_AndIsBannerRow_ClassifyThreeAndFourEqualsRowsIdentically, the assertion that reads like the consistency guard —creationPath.Should().Be(filingPath)— still passes under the prohibited widening edit, because both sides becometruetogether. Only the siblingcreationPath.Should().BeFalse(...)line catches the relaxation. This was verified directly against the dispatch path rather than inferred.Reachability
The underlying defect is latent, not live. No producer emits a three-character row today, so no user-visible misbehaviour was reachable before this change, and none is fixed by it. The cost was entirely to the next maintainer: the inaccurate comment invited a contributor to "correct" the guard in the dangerous direction. This PR is therefore a maintainability change, and it should be weighted as one.
What changed
Four files, 57 insertions and 9 deletions:
QuickFiler/Controllers/EfcSelectionGuard.csBannerRejectionPrefix, value unchanged; bothStartsWithcall sites updated; XML doc rewritten to state that it is deliberately a proper prefix of the producers' constant and must not be widenedQuickFiler/Controllers/EfcFormController.csSelectedFoldercomment replaced so it describes the composition the code implements. Comment-only; no executable line changedUtilitiesCS/OutlookObjects/Folder/FolderSuggestionTree.csBreadcrumbRowBuilder.BannerPrefixdirectlyQuickFiler.Test/Controllers/EfcSelectionGuardTests.cs[TestMethod]added, pinning that both arities are rejected on both predicates, with abecausemessage naming the prohibited directionThe constant is deleted rather than re-aliased because an aliasing declaration would still match the declaration inventory and defeat the criterion it is meant to satisfy.
Verification
All ten acceptance criteria pass and are checked off in
issue.md. Feature review reports zero blocking findings across the policy audit, code review, and feature audit./t:Rebuildand without/p:Nullable=enable.QuickFiler.Testmoved 1286 to 1287 passed, the single added test;UtilitiesCS.Testunchanged at 4783.<Counters/>elements in the committed evidence rather than from console summaries.Defects found and handled during delivery
Stale diff anchor in three plan gates. The plan pinned a base SHA that is an ancestor of both this branch and
origin/main, so the two-dotgit diffform those gates use reported everythingorigin/mainhad accumulated since that commit alongside this branch's own work. Measured before any edit existed, the scope gate returned 22 paths against an asserted union of 4. The anchor was replaced withgit merge-base origin/main HEAD, resolved at run time; the same gate then returns empty pre-edit.Committed TRX evidence was not well-formed XML. The artifact-hygiene sweep substitutes angle-bracket placeholders, and
vstestwrites those redacted values into XML attribute values, where a raw angle bracket is illegal. All six committed TRX files failed to parse. They were repaired by escaping the placeholders, which leaves the redaction identical and restores parseability. The hygiene gate did not catch this because it asserts only that host identifiers were removed, never that the rewritten file still parses.Note on the generated PR-context bundle
Two fields in
artifacts/pr_context.summary.txtare wrong and were not relied on:#465,#498,#662and#ISO-8601.#ISO-8601is not an issue number at all but a token scraped from evidence-convention prose, and#465and#498are unrelated issues cited as precedent in the requirements, both already closed. Each was checked individually withgh. Only#662is in scope for this PR.Base, head, and merge-base in the bundle were correct.
Follow-ups recorded, not filed
This item carries footprint acceptance criteria restricting the branch diff, so no follow-up issue was opened from this branch. These are recorded in the audit artifacts for consolidated filing after merge:
origin/mainhas not touched either protected file since that commit.FolderSuggestionTree.IsBanner's doc comment still hard-codes the four-character literal rather than naming the constant.EfcFormController.csis 1189 lines and at 25.5% line coverage. Pre-existing, unchanged by this PR, and inside the ratified Outlook Interop exemption class; recorded as a FAIL row in the audit rather than silently passed.GitHub Auto-close
Emitted from the author-asserted autoclose list after verifying the number directly with
gh. The other three tokens on that list were checked and rejected as described above.🤖 Generated with Claude Code
https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL