Skip to content

fix(quickfiler): narrow ProcessCmdKey Alt-chord claim to bare Alt (#663) - #722

Merged
drmoisan merged 15 commits into
mainfrom
bug/qfc-twin-processcmdkey-alt-chord-over-claim-663
Sep 1, 2026
Merged

drmoisan merged 15 commits into
mainfrom
bug/qfc-twin-processcmdkey-alt-chord-over-claim-663

Conversation

@drmoisan

@drmoisan drmoisan commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • QfcFormViewer.ProcessCmdKey claimed every Alt-bearing key chord, so Alt+M never reached WinForms mnemonic resolution and the hosted item viewer's &Move Options menu could not be opened from the keyboard. Alt+F4 was consumed for the same reason.
  • The claim decision moves into a new internal static bool QfcFormKeyHandler.ClaimsAltChord(IQfcKeyboardHandler handler, Keys keyData) that masks the key-code half of the key value and claims only Keys.Menu or Keys.None. Every other Alt chord now falls through to base.ProcessCmdKey.
  • This is the QuickFiler twin of the Email Filer fix delivered as issue Bug: efc-viewer-processcmdkey-swallows-alt-mnemonics #467 under feature Bug: efc-controllers-null-guard-and-async-void-boundary-defects #464, which deliberately left the QuickFiler surface untouched for file-ownership reasons.
  • Seven new MSTest methods pin all eight rows of the predicate's behavior table. Three of them were captured failing before the fix and passing after it, in real runs rather than by assertion.
  • The production and test change set is exactly three files. Neither .csproj is edited, no file is added or removed, and no [ExcludeFromCodeCoverage] attribute is introduced.

Why

QuickFiler/Controllers/QfcFormKeyHandler.cs tested only the Alt modifier bit and never inspected the key-code half of the key value. Keys.Alt is the modifier bit 262144; Keys.KeyCode is the mask 65535 that isolates the virtual-key code. A real bare Alt press masks to Keys.Menu (18, documented as "The ALT key") and the synthetic Keys.Alt value used in unit tests masks to Keys.None. Any other key-code value means a second key was pressed with Alt, which is a mnemonic or a system chord and not the keyboard-dialog gesture.

A ProcessCmdKey override that returns true suppresses message dispatch before WinForms mnemonic resolution runs in ProcessDialogChar / ProcessMnemonic, so the mnemonic carried by the consumed chord never fired. The form dispatches the parameterless ToggleKeyboardDialogAsync(), whose signature accepts no key data at all — so the only gesture it can encode is a bare Alt press, and claiming any other Alt chord consumed a key the form would not act on.

Correction to the issue text carried into this PR. The issue body says "Alt+F and Alt+M are swallowed". Alt+M is correct; Alt+F is not. The QuickFiler form Designer declares no MenuStrip, no ToolStripMenuItem, and no ampersand in any of its six .Text = assignments — ButtonFilters.Text is the plain string "Filters". The "Alt+F" wording was carried over from the Email Filer twin, whose form does carry a &Filters mnemonic. No acceptance criterion asserts that Alt+F opens a menu on this surface.

What Changed

Core fix (2 production files)

  • QuickFiler/Controllers/QfcFormKeyHandler.cs — adds ClaimsAltChord, plus the using QuickFiler.Interfaces; directive its parameter type requires. IsAltKeyCommand is unchanged: it is not narrowed, not renamed, and not deleted.
  • QuickFiler/Viewers/QfcFormViewer.cs — the four-line ProcessCmdKey guard collapses to a single predicate call. The branch body, including the parameterless ToggleKeyboardDialogAsync() dispatch, is unchanged.

Tests (1 file)

  • QuickFiler.Test/Controllers/QfcFormKeyHandlerTests.cs — seven new ClaimsAltChord_* methods using MSTest, Mock<IQfcKeyboardHandler>, and a FluentAssertions because-string on every assertion. The four existing IsAltKeyCommand_* methods are untouched.

Docs and evidence

  • The feature folder under docs/features/active/qfc-twin-processcmdkey-alt-chord-over-claim-663/ carries spec.md (the sole acceptance-criteria source for this full-bug item), issue.md, the research record, the atomic plan, the three review artifacts, and the Phase 0 through Phase 6 evidence.
  • One promotion record, docs/features/potential/promoted/2026-08-31-invoke-mstest-single-assembly-strictmode-count-throw.md, rides along deliberately. It belongs to issue Bug: invoke-mstest-single-assembly-strictmode-count-throw #713, opened during this feature's preparation for a test-runner defect that this fix does not touch.

Architecture / How It Fits Together

Control.ProcessCmdKey bubbles up the control hierarchy, so the form-level override intercepts Alt+M typed anywhere on the form, including inside any hosted ItemViewer. Additional ItemViewer instances are manufactured per loaded row, each carrying its own &Move Options mnemonic.

The predicate is placed on QfcFormKeyHandler rather than on the viewer, deviating from the Email Filer precedent, for two reasons. First, coverage measurability: the viewer types carry [ExcludeFromCodeCoverage] while QfcFormKeyHandler does not, so only this placement produces a <method> element in the Cobertura output and lets the >= 90% new-method floor be demonstrated by measurement rather than by assertion. Second, the type's own XML summary already describes it as holding "pure routing predicates extracted from the QuickFiler form variants' ProcessCmdKey overrides so the key-command logic can be unit tested without a live Form window handle".

IsAltKeyCommand is retained rather than narrowed because its breadth is semantically meaningful for the other dispatch contract that still references it. The three uncompiled viewer variants call a KeyboardHandler_KeyDown(object, KeyEventArgs) overload that dispatches on e.KeyCode, and Keys.Left is a registered KeyActions entry there. Adding a separate, narrowly scoped predicate leaves that contract untouched.

Verification

Completed

Gate Result
dotnet tool run csharpier check . exit 0, Checked 1566 files
msbuild TaskMaster.sln /t:Rebuild /m ... /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true exit 0, 0 Error(s), 36 occurrences of Task "Csc"
msbuild TaskMaster.sln /t:Rebuild /m ... /p:TreatWarningsAsErrors=true exit 0, 0 Error(s), 36 occurrences of Task "Csc"
scripts/vscode/Invoke-MSTest.ps1 -SearchRoot . -Configuration Debug 6934/6934 passed, 0 failed, 0 not-run
Coverage, repository-wide line 85.3726% post-change against 85.3866% pre-change
Coverage, ClaimsAltChord line-rate 1, branch-rate 1, against the >= 0.90 new-method floor
Feature review 0 blocking findings; 15 of 15 acceptance criteria PASS

Fail-before / pass-after is a real observation, not an assertion. Phase 1 landed a behaviour-preserving seam so the new tests would compile, which turned what would have been a build break into a genuine runtime red. The Phase 2 run failed exactly three tests — ClaimsAltChord_WithAltM_ReturnsFalse, ClaimsAltChord_WithAltF4_ReturnsFalse and ClaimsAltChord_WithAltLeft_ReturnsFalse, all on declaring type QuickFiler.Controllers.Tests.QfcFormKeyHandlerTests — and the Phase 3 run after the key-code mask passed all 6934.

Test names are identified by declaring type throughout, because QuickFiler.Test already declares ClaimsAltChord_WithAltM_ReturnsFalse and ClaimsAltChord_WithNullHandler_ReturnsFalse on the Email Filer fixture. A bare method name is not unique in this assembly.

Not verified in this PR. The live-host gesture check is recorded as MANUAL_CHECK_DEFERRED, with both justifying probes measured (Get-Process -Name OUTLOOK returned 0; [Environment]::UserInteractive returned True). The claim decision is proven by unit test; what is unverified is the downstream WinForms behaviour — that returning false for Alt+M actually opens the focused row's menu, and that Alt+F4 actually closes the window. AC-15 was written to accept this deferral and to reject a silent pass. Feature #464 recorded the same disposition for the Email Filer twin.

Recommended

pwsh -NoProfile -File scripts/vscode/Invoke-MSTest.ps1 -SearchRoot . -Configuration Debug

In a live Outlook session with the QuickFiler form open and at least one loaded row: press Alt (the keyboard-navigation dialog should toggle, unchanged), then Alt+M with a row focused (the Move Options menu should open), then Alt+F4 (the window should close).

Backward Compatibility / Migration Notes

No breaking changes. QfcFormKeyHandler is internal and the new member is internal static; there is no public API change. The test assembly reaches the internal member through the existing [assembly: InternalsVisibleTo("QuickFiler.Test")], so no project-file or attribute change was needed.

The observable behaviour change is a strict narrowing of what the form claims. Bare Alt is unchanged. Alt+M, Alt+F4 and Alt+arrow now fall through to the base implementation instead of toggling the keyboard dialog. Rollback is a revert of the commit.

Risks and Mitigations

Risk Reachability Mitigation
Duplicate-mnemonic ambiguity: with N loaded rows there are N+2 controls owning &Move Options, and WinForms cycles focus among controls sharing a mnemonic, so the first Alt+M press may not open the intended row's menu. Unresolvable statically; the visibility and enabled state of the Designer-held templates at runtime are not determinable from source. This is what the deferred live-host check is for. If the wrong menu opens, that is a distinct mnemonic-ownership defect and belongs in a follow-up issue, not in this fix.
Removing the last compiled consumer of IsAltKeyCommand could trip an unused-member analyzer. Did not materialize. The analyzer build produced zero warnings naming any of the three changed files, measured against an equally empty Phase 0 baseline. The member is internal in an assembly with InternalsVisibleTo and retains four test consumers.
Regressing the bare-Alt keyboard-dialog toggle. Contained. Pinned in both key-data shapes: Keys.Alt (masking to Keys.None) and Keys.Menu | Keys.Alt (masking to Keys.Menu, the shape a physical keyboard produces). The Email Filer suite pins only the synthetic shape; this one pins both.
Keys.None still admits Alt+Shift and Alt+Ctrl; Keys.Menu still admits AltGr. Latent and not a regression — all of these were claimed before this change too. The change strictly narrows the claim set, and AltGr+letter is newly released. No action recommended.

Review Guide

Suggested order:

  1. QuickFiler/Controllers/QfcFormKeyHandler.cs — the whole fix is the new predicate; nine lines of logic.
  2. QuickFiler/Viewers/QfcFormViewer.cs — a four-line guard becomes one line.
  3. QuickFiler.Test/Controllers/QfcFormKeyHandlerTests.cs — seven added methods.
  4. docs/features/active/qfc-twin-processcmdkey-alt-chord-over-claim-663/spec.md for the acceptance criteria, then the three review artifacts.

The remaining diff is evidence and documentation and is mechanical.

Two things a reviewer should not read as oversights. The unused locals object sender = FromHandle(msg.HWnd) and var e = new KeyEventArgs(keyData) inside the claimed branch are pre-existing, unrelated to the claim decision, and retained deliberately — the bugfix policy requires the minimal targeted fix and forbids opportunistic refactors. And IsAltKeyCommand now has zero compiled production consumers but is retained on purpose, because narrowing or deleting it would change the contract for the uncompiled call sites that still reference it.

Two stale prose citations, both documentation-only and neither load-bearing. spec.md and the plan cite QuickFiler.Test/QuickFiler.Test.csproj:151 for the test file's compile entry, which is now line 152 after main added a line above it. AC-14 cites QfcFormViewer.cs:64-67 for the retained locals, which are now at lines 61-64 because the guard collapsed from four lines to one. No acceptance condition asserts a line number; AC-14 matches on content.

Follow-ups

Reported here rather than filed, because this PR carries a footprint acceptance criterion pinning its .cs diff to exactly three paths. They will be filed as one consolidated issue from a separate branch after merge.

  1. Removal of the unused locals in QfcFormViewer.ProcessCmdKey.
  2. TaskVisualization/TaskViewer.cs discards the bool returned by TaskController.KeyboardHandler_KeyDown at one call site and consumes it at another. Whether that inconsistency is a live defect is unresolved and belongs to the TaskVisualization project.
  3. Add the missing Keys.Menu | Keys.Alt positive case to the Email Filer suite. This is the most valuable of the four: the Keys.Menu arm of the delivered Bug: efc-viewer-processcmdkey-swallows-alt-mnemonics #467 predicate is currently deletable without failing any test, which this branch's own suite demonstrates is worth pinning.
  4. The Invoke-MSTest.ps1 single-assembly StrictMode throw, already opened as issue Bug: invoke-mstest-single-assembly-strictmode-count-throw #713 and explicitly out of scope here.
  5. The PR-context tooling misclassifies these three .cs files as documentation and reports Core logic changes: 0 files, which leaves the C# coverage enforcement hook with an empty changed-language set and silently skips it. Confirmed by dot-sourcing the hook.

Known tooling defect affecting this PR's own context bundle. artifacts/pr_context.summary.txt reports the GitHub CLI unavailable, which is false — gh works in this environment and was used to verify every issue number below. Its author-asserted auto-close list additionally contains 22 tokens, of which only #663 is a real, in-scope issue: #AC-1 through #AC-15, #SHA-256, #VC-1 and #VC-2 are acceptance-criterion and verification-command identifiers scraped from prose, and #464, #467 and #469 are cited only as precedent. #713 is open and explicitly out of scope. That list was not used.

GitHub Auto-close

drmoisan and others added 15 commits August 31, 2026 20:24
…scope evidence

Preparation-only work for issue 663, the QuickFiler twin of the EFC
ProcessCmdKey Alt-chord over-claim. No production code is changed.

Adds the active feature folder, an issue.md authored from the GitHub issue
body, and a scope-determination evidence artifact.

The evidence artifact records the load-bearing fact that the textual and the
compiled call-site counts of QfcFormKeyHandler.IsAltKeyCommand differ:
QuickFiler.csproj is a non-SDK project with no compile glob, and neither
QfcFormViewerDark.cs nor QfcFormViewerExpanded.cs nor any file under
QuickFiler/Legacy/ appears in its item list. The predicate therefore has
exactly one compiled consumer, QuickFiler/Viewers/QfcFormViewer.cs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…r surface

The issue body states that Alt+F and Alt+M menu mnemonics are swallowed. That
sentence was carried over from the EFC twin and is half wrong here.

QfcFormViewer.Designer.cs declares no MenuStrip, no ToolStripMenuItem and no
ampersand in any of its six Text assignments; ButtonFilters.Text is the plain
string Filters, where the EFC counterpart is &Filters. There is no Alt+F
mnemonic on this surface.

The real mnemonic is Alt+M, on the &Move Options MenuStrip item carried by the
hosted ItemViewer and ItemViewerExpanded user controls. The defect is real, but
an acceptance criterion written from the issue body would have asserted a
mnemonic that does not exist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Records the WinForms routing semantics, the QFC key-action registry contents,
the reason feature 464 left the shared predicate alone, the test-project
mechanics, and the coverage posture of each candidate predicate host.

Two findings change the scope: feature 464 skipped QfcFormKeyHandler.cs for
file-ownership reasons rather than any technical judgement, and no key-action
registry on the QuickFiler surface holds an Alt-modified key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…ates

Adds the full-bug spec with a 15-row acceptance-criteria table, the invariant
statement, and a disposition row for every one of the five ProcessCmdKey Alt
claim sites in the solution.

Orchestrator review of the drafted spec found and fixed four defects before
preflight:

- AC-12 and AC-14 wrote their Select-String alternations as escaped pipes. In
  a .NET regex an escaped pipe is a literal pipe, so the pattern matched
  nothing: AC-12 passed vacuously and AC-14 was unsatisfiable. Verified against
  a fixture, 0 matches escaped versus 2 unescaped. The patterns moved into a
  fenced Verification command reference, because a bare pipe also terminates a
  markdown table cell.
- AC-10 demanded exit code 0 from the repository-wide coverage wrapper, which
  throws on any inner failure and so could not pass against the known
  pre-existing load-driven failures. Regated on baseline-set membership, plus a
  Task Csc non-vacuity observation proving CoreCompile actually ran.
- AC-11 wrote coverage artifacts to an evidence/coverage/ path that is not a
  member of the canonical evidence sub-path set. Moved to evidence/qa-gates/.
- AC-15 required a positive live-host confirmation no automated executor can
  produce. Regated on the feature 464 precedent, which records
  MANUAL_CHECK_DEFERRED with measured probes rather than a silent pass.

Every git diff anchor moved to the three-dot merge-base form so a moving
origin/main cannot bill another change to this one, and AC-14 gained a
porcelain-status companion because a name-listing diff is blind to a new file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…fect as 713

Adds the 7-phase, 57-task atomic plan. The plan validator passes.

The plan splits the fix into three phases rather than the usual two, because
the seven new tests cite a member that does not exist at branch head: adding
them first breaks the whole QuickFiler.Test assembly and yields no per-test
evidence. Phase 1 lands the predicate in a form equivalent to the current
guard, Phase 2 runs the expect-fail pass against three named tests, Phase 3
adds the key-code mask.

Also corrects a clause in spec.md that the plan proved wrong. AC-10 required
the scoped Invoke-MSTest.ps1 -SearchRoot QuickFiler.Test run to exit 0. That
run cannot succeed: the script sets StrictMode Latest, its discovery pipeline
unrolls to a bare string when exactly one assembly matches, and the following
guard reads Count on it. Verified directly under pwsh, where Count on a scalar
string raises PropertyNotFoundException. Every wrapper invocation now uses the
repository-wide search root.

That defect is unrelated to this fix and would have vanished at merge as prose,
so it was promoted to its own issue rather than described in the feature
folder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
P0-T11 previously referred to an unnamed follow-up issue. The issue now exists,
so the task names it and states that the defect is out of scope for this fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Preflight returned REVISIONS REQUIRED with twelve defects. All twelve are
applied. The plan validator still passes; the phase and task counts are
unchanged at 7 and 57.

The two most consequential propagate into spec.md, not just the plan:

- A gate reading no console line containing error cannot pass. A successful
  MSBuild run prints /errorreport:prompt on every Csc command line and prints
  its own 0 Error(s) summary. Regated on the MSBuild diagnostic form.
- Two of the seven new test method names already exist in QuickFiler.Test, on
  the Email Filer fixture at EfcViewerTests.cs lines 134 and 156. A bare method
  name is therefore not a unique identifier in a test run's output, so every
  named-test reading now carries the declaring type.

Also corrected: an unscoped diff already matched ExcludeFromCodeCoverage twenty
times before any source edit; the instrumented coverage run was gated against
an uninstrumented baseline failure set; the terminal clean-tree gate asserted
over its own outputs; the formatter's distinguishing observation did not
distinguish; the Keys.Control row of the behavior table had no test, so AC-1
was unestablishable; and the restore step had no fallback for the known
MSBuild-restore shortfall recorded by feature 469.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Round 2 returned REVISIONS REQUIRED with five defects, D13 through D17. All
five are applied. The plan validator still passes; phases and tasks remain 7
and 57, and the spec still carries 15 criteria in both table and checklist.

Two of the five are incomplete applications of round 1's deltas by this
orchestrator, not new planner defects:

- D15: round 1's D3 named three edits. Only the two task edits were applied;
  the reading-guide paragraph stating the same rule was missed, so the guide
  still described one baseline failure set for every later run while P4-T6
  compared against a second one.
- D14: round 1's D2 scoped the plan's diff to .cs paths but the matching spec
  AC-13 verification was left unscoped, where it reports twenty prose matches
  and could never pass.

The other three are genuine plan defects. D13 is the most serious: both
analyzer-concordance searches in P0-T5 failed to execute, because a backslash
doubles down to an unbalanced group when handed to the native pwsh executable
and because an escaped double quote is not an escape sequence inside a
PowerShell double-quoted string. Both now spell those characters as the regex
hex escapes x5C and x22, which are inert to every intervening quoting layer;
both replacements were executed against this worktree before being adopted.

D16 ordered the P6-T19 self check-off before its amend, since performing it
afterwards reopens the uncommitted state that task exists to close. D17 added a
change detector for the Keys.Control assertion AC-5 requires, whose omission
previously altered no acceptance condition anywhere in the plan; the
branch-head count of exactly one match was measured directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Adds three new orchestrator memories and extends one existing memory with a
converse case observed during this run:

- Get-BlastRadius harvests backtick-delimited paths from spec.md, not just the
  plan, so a backticked comparison path enters the computed radius and can
  serialize a parallel item against unrelated work.
- Two Select-String quoting traps. An escaped pipe is a literal pipe, which
  makes a zero-match assertion pass vacuously; measured 0 escaped versus 2
  unescaped. A doubled backslash and an escaped double quote both die in transit
  to pwsh, so the hex escapes x5C and x22 are the portable spellings.
- A successful msbuild run prints the word error about thirty-five times through
  errorreport:prompt plus its own zero-errors summary, so a gate forbidding that
  word is unsatisfiable rather than strict.
- The active-folder MCP tool emits no issue.md at all when no promoted source
  exists, which leaves the folder without the work-mode marker.

Also records that applying only part of a multi-part preflight delta cost a full
extra round on this item, twice.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…he rounds

Resumes the interrupted preparation run and drives the plan to clearance.
Preflight round 7 returned PREFLIGHT: ALL CLEAR with CONVERGENCE: NO FURTHER
ROUNDS EXPECTED. The plan validator returns ok true with no warnings.

Rounds 3 through 7 ran in this pass. Defect counts across all seven rounds fell
12, 5, 11, 10, 3, 1, 0. Every revision was applied in place at the canonical
plan path, per the Plan-Path Continuity Contract. No timestamped sibling plan
file was created.

The six blocking defects were all unsatisfiable or self-contradictory acceptance
conditions:

- The SDK bootstrap gate asserted over a command that enumerates the host muxer
  root and does not consult global.json, so it could never report the
  repo-local SDK.
- An absolute-zero analyzer-warning clause contradicted the baseline-relative
  clause applied to the same command, and was unsatisfiable if such a warning
  already existed on the base branch.
- The terminal commit-and-amend sequence had no fixpoint, twice. First because
  the post-amend observation re-dirtied the file the amend had just committed.
  Then because the residue list omitted the artifact the preceding task
  necessarily writes after its own commit, since that artifact records the
  commit's own exit code.
- The formatter baseline had no failure branch, so the final format gate was
  internally contradictory whenever that baseline was non-zero.
- A required observation was false against the tree. EfcViewer.cs line 96
  declares a predicate of the same name as the one under test and that file is
  a compile item, so the unqualified absence assertion could not be recorded
  truthfully. Scoping it to the intended class closes it.

Two spec claims were also false and are corrected here. The coverage wrapper
does not share the single-assembly discovery defect, because its discovery
pipeline is wrapped in an array subexpression and so is array-valued at any
match count. And the plan asserted a deviation from the spec on search-root
form where it in fact complies.

Clearance evidence is recorded under the item's own evidence folder, so the
signal is auditable rather than inferred. The absence of any such record is what
made the interrupted run indistinguishable from an uncleared one.

No plan task was executed. No production or test source file was modified.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…mdkey-alt-chord-over-claim-663

# Conflicts:
#	.claude/agent-memory/atomic-planner/MEMORY.md
#	.claude/agent-memory/task-researcher/MEMORY.md
QfcFormViewer.ProcessCmdKey claimed every chord carrying the Keys.Alt
modifier, because QfcFormKeyHandler.IsAltKeyCommand tests only that bit.
A ProcessCmdKey override that returns true suppresses message dispatch
before WinForms mnemonic resolution runs, so the "&Move Options" Alt+M
mnemonic on the hosted ItemViewer and ItemViewerExpanded controls never
opened its menu, and Alt+F4 never closed the window.

Add QfcFormKeyHandler.ClaimsAltChord(IQfcKeyboardHandler, Keys), which
accepts the Alt modifier only when keyData & Keys.KeyCode is Keys.Menu
or Keys.None, and route the viewer guard through it. The dispatch stays
the parameterless ToggleKeyboardDialogAsync(), whose only encodable
gesture is a bare Alt press.

IsAltKeyCommand is unchanged and keeps its four existing tests: its
breadth is still meaningful for the KeyEventArgs dispatch contract that
the uncompiled viewer variants reference.

Seven MSTest methods are added to the existing QfcFormKeyHandlerTests
fixture. Three of them failed before the fix and pass after it.

Refs #663

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
…nce criteria

Adds the Phase 0 baseline, Phase 2 and 3 regression-testing, Phase 4 QA-gate,
Phase 6 manual-validation and issue-update evidence artifacts for the QuickFiler
ProcessCmdKey Alt-chord over-claim fix, and marks AC-1 through AC-15 delivered in
spec.md.

Refs #663

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
Adds the policy-audit, code-review and feature-audit artifacts for the QuickFiler
ProcessCmdKey Alt-chord over-claim fix. The review reports zero blocking findings
and confirms all fifteen acceptance criteria on evidence rather than on the
executor's check-off. The C# coverage row records an honest FAIL for the absent
canonical coverage XML alongside a PASS on every measured threshold.

Refs #663

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYLDoRLKXS5sgAzegW7ZL
@drmoisan
drmoisan merged commit 988d35a into main Sep 1, 2026
5 checks passed
@drmoisan drmoisan mentioned this pull request Sep 2, 2026
1 of 5 tasks
@drmoisan
drmoisan deleted the bug/qfc-twin-processcmdkey-alt-chord-over-claim-663 branch September 2, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: qfc-twin-processcmdkey-alt-chord-over-claim

1 participant