fix(quickfiler): repair three keyboard-action contract defects (#445) - #587
Merged
drmoisan merged 2 commits intoAug 22, 2026
Conversation
Closes#445. Defect 1 - inconsistent `Activated` gating. `KaStringAsync.KeyEquals` branch 3 (`other.Length > 1`) invoked `Update` without the `Activated` guard that branches 1 and 2 apply, so a non-matching multi-character probe fired its side effect on every LINQ re-enumeration within one keystroke. The guard is now `if (Activated && Update is not null)` in all three branches. Branch 1's early return is deliberately preserved: `KeyboardHandler` re-arms the latch only at filter length 1 and then makes three passes per keystroke, so clearing the latch on a match would stop the item-number label advancing. A new test pins that behavior. Defect 2 - `KeyEquals("")` had no defined contract. `Key.Contains("")` is true for every receiver, so an empty probe entered branch 1 and, with `Activated` true and a non-null `Update`, evaluated a substring offset of -1 and threw `ArgumentOutOfRangeException`; with the guard false it silently returned true, matching every registered action. `KeyEquals` now rejects null with `ArgumentNullException` and empty with `ArgumentException` from a guard clause above the `Contains` test, so the negative start index is unreachable. The `KbdActions` string methods inherit this precondition, which is documented in-code. Defect 3 - `KaChar.DelegateType` returned `typeof(Action<Keys>)` while the type stores an `Action<char>`. `DelegateType` was orphaned public API on `KaChar` and `KaKey` with its interface member commented out, so it is removed rather than corrected; a repository-wide search over `*.cs` now returns zero hits. The dead `Update` property is removed from `KaChar`, `KaCharAsync`, `KaKey`, and `KaKeyAsync` and retained on `KaStringAsync`, which reads it. Both commented-out members are deleted from `IKbdAction`, whose four live members are unchanged, so no implementer signature moves. The non-prefix `Substring` offset in branch 1 is a fourth, distinct defect whose fix is a keyboard-filtering behavior change. It is out of scope here and filed as #583. Verification: csharpier 1517 files, 0 needing format; msbuild analyzer and nullable gates both exit 0 with 0 errors and 5 pre-existing third-party advisories, `Skipping target "CoreCompile"` count 0 in both; vstest 6441 passed, 0 failed, 0 skipped across 9 assemblies (baseline 6437, delta is the 4 new tests). New production line coverage 12/12. `KaChar.cs` and `KaKey.cs` rise to 100% by shedding the removed dead lines. No file exceeds 500 lines. `QuickFiler.Test.csproj` is untouched; all new tests land in files that already carry compile entries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the three review artifacts for issue #445 produced against commit 1292b4c: policy-audit, code-review, and feature-audit, all timestamped 2026-08-22T11-30. Outcome: 0 Blocking and 0 blocking-PARTIAL findings, all 21 acceptance criteria PASS, ready to merge. Four advisory findings are recorded and dispositioned non-blocking: the coverage-delta flipped lines are not localized, AC18's literal text diverges from the agent-memory carve-out the plan grants at P4-T3, two pre-existing unused usings survive in KaChar.cs and IKbdAction.cs, and the repository-wide coverage shortfall predates this change. The review independently traced the blast radius of the new empty-probe precondition and found no production path that reaches the throw: keyboard filter probes are always length 1 or greater because the append precedes the probe, indexer probes use registered non-empty keys, and Remove and Add route through StoredKeyEquals rather than KeyEquals. No remediation-inputs artifact was produced because no cycle was required. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
drmoisan
merged commit Aug 22, 2026
577270d
into
epic/quickfiler-suite-determinism-foundation-integrationdrmoisan added a commit
that referenced
this pull request
Aug 22, 2026
Children 449 (PR #585, follow-up #590), 445 (PR #587), and 491 (PR #588) are merged; each merge commit was confirmed reachable from the fetched integration head rather than taken from a completion notification. Child 511 remains in atomic execution. Records seven carried findings, two of which correct this epic's own inputs: epic.md misattributed QuickFiler/Legacy/QuickFileController.cs's 1,065 lines to QuickFiler/Controllers/QfcExplorerController.cs (182 lines after change, and the legacy file has zero compile references), and collect_pr_context writes into the shared main checkout, letting one child overwrite a sibling's PR context (issue #589). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHj7wjLweuwfAP8NDA4iiP
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 freeto 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
Repairs three contract defects in the QuickFiler keyboard-action family. Closes#445.
This is a wave-0 child of the
quickfiler-suite-determinism-foundationepic and targets the epic integration branch, notmain.Defect 1 — inconsistent
Activatedgating inKaStringAsync.KeyEqualsBranches 1 and 2 gated their side effects on
Activated; branch 3 (other.Length > 1) invokedUpdatewithout the gate. A non-matching multi-character probe therefore fired its side effect on every LINQ re-enumeration within a single keystroke. All three branches now gate onActivated && Update is not null.Branch 1's early return is deliberately preserved rather than "completed" into a fall-through to the trailing latch reset.
KeyboardHandlerre-arms the latch only at filter length 1 and then makes three passes per keystroke; if a matching probe cleared the latch, the first pass would consume the activation and the item-number label would stop advancing. A new test pins that behaviour, and the rationale is recorded in the method's XML documentation.Defect 2 —
KeyEquals("")had no defined contractKey.Contains("")istruefor every receiver, so an empty probe entered branch 1 and, withActivatedtrue and a non-nullUpdate, evaluatedKey.Substring(other.Length - 1, 1)with a start index of-1and threwArgumentOutOfRangeException. With the guard false it silently returnedtrue, so an empty probe matched every registered action.KeyEqualsnow rejectsnullwithArgumentNullExceptionand empty withArgumentException, from a guard clause placed above theKey.Contains(other)test. The negative start index is unreachable.Defect 3 —
KaChar.DelegateTypereported a type it does not storeKaCharstores anAction<char>butDelegateTypereturnedtypeof(Action<Keys>). Because the corresponding interface member was commented out,DelegateTypewas orphaned public API onKaCharandKaKeywith no caller, so it is removed rather than corrected. A repository-wide search over*.csnow returns zero hits.The dead
Updateproperty and its backing field are removed fromKaChar,KaCharAsync,KaKey, andKaKeyAsync, and retained onKaStringAsync, which reads it. Both commented-out members are deleted fromIKbdAction; its four live members are byte-identical, so no implementer signature moves.Behaviour change and blast radius
The empty-probe precondition is a real behaviour change: the
KbdActionsstring-keyed members (ContainsKey,FilterKeys,Find,FindIndex, and the indexer) inherit it, and an empty key argument now surfaces anArgumentExceptionrather than matching everything.Review traced every live caller and found no production path that reaches the new throw:
KeyboardHandler.cs:180precedes the probes at:181and:188.digitsis 1 or 2 on every path).RemoveandAddroute throughStoredKeyEquals, notKeyEquals.The consequence is documented in the
KeyEqualsXML documentation so a future caller is warned at the API surface.Out of scope, and filed
Branch 1 guards on
Key.Contains(other)but computesKey.Substring(other.Length - 1, 1), an offset that is only meaningful whenotheris a prefix ofKey. This is a fourth, distinct defect whose resolution requires choosing betweenContainsandStartsWith— a keyboard-filtering behaviour change currently pinned by an existing test. It is deliberately not fixed here and is filed as #583.Verification
No GitHub Actions workflow runs on this pull request:
.github/workflows/ci.ymltriggerspull_requestonly on[main, development], and this PR targets the epic integration branch. The absence of checks is expected and is not a failure. The green gate is the full local C# toolchain, run in policy order to a single uninterrupted clean pass.csharpier check ./t:Rebuild)/t:Rebuild)/InIsolation, coverage)Both MSBuild logs record a
Skipping target "CoreCompile"count of 0 (andCoreCompile:counts of 100 and 111), which proves the analyzer and nullable gates actually compiled rather than short-circuiting on an incremental up-to-date check. The test baseline before the change was 6437 passed / 0 failed; the delta of exactly +4 is the four new tests.Coverage
Per file:
KaStringAsync.cs49/49 to 60/60;KaChar.cs28/33 to 28/28;KaKey.cs28/33 to 28/28;IKbdAction.cs0/0 (interface-only, no executable line).KaChar.csandKaKey.csreach 100% by shedding the five uncovered lines each that the removed dead API occupied.The blocking gates pass: new production line coverage 12/12 = 100% against a 90% requirement, and no changed-line regression. The repository-wide rates remain below the 80% and 85% floors; that shortfall was measured before any edit in this change, is pre-existing and unadjudicated, and both rates moved upward here.
Review outcome
Feature review produced
policy-audit,code-review, andfeature-auditartifacts dated2026-08-22T11-30, all committed.spec.md(21 checked, 0 remaining).The four advisory findings are recorded and dispositioned non-blocking: coverage-delta flipped lines are not localized; AC18's literal text diverges from the agent-memory carve-out the plan grants at P4-T3; two pre-existing unused
usingdirectives survive inKaChar.csandIKbdAction.cs; and the repository-wide coverage shortfall predates this change.One arithmetic discrepancy was disclosed rather than smoothed:
lines-validreconciles exactly (+11 −5 −5 = +1) butlines-coveredmoved +6 where the in-scope files account for +11. Review adjudicated the disclosure adequate and not masking a regression — all four in-scope files measure 100% per file after the change, the magnitude is 0.006 percentage points, within the knowndotnet-coveragerun-to-run band, and the instrumented run reproduced 6441/6441 exactly.Scope constraints honoured
QuickFiler.Test/QuickFiler.Test.csprojis unchanged — siblings Bug: quickfiler-test-form1-live-form #491 and Bug: quickfiler-explorer-controller-latent-defects #449 own regions of it. All four new tests land in files that already carry<Compile Include>entries..claude/**except three.claude/agent-memory/atomic-executor/**files, which the plan explicitly carves out at P4-T3.docs/features/potential/**,.github/workflows/**,config/blast-radius.json, orconfig/orchestration-routing.json.KbdActions.cs,KeyboardHandler.cs, andQfcCollectionController.csare unmodified.KaChar.cs,KaKey.cs, andIKbdAction.csare each shorter than before.DateTime.Now,Random.Shared,Thread.Sleep,Task.Delay) appears in any of the five changed source files.Files changed
Production and test sources (5):
QuickFiler/Controllers/KaStringAsync.csQuickFiler/Controllers/KaChar.csDelegateType, twoUpdateproperties, unusedusingQuickFiler/Controllers/KaKey.csDelegateType, twoUpdateproperties (usingretained;Keysis still its key type)QuickFiler/Interfaces/IKbdAction.csQuickFiler.Test/Controllers/KaStringAsyncTests.csThe remaining 38 files are the audit trail: 30 evidence artifacts, the three review artifacts, the plan and spec checkbox updates, and three agent-memory notes.
🤖 Generated with Claude Code