Summary
QfcCollectionController.RegisterNavigation captures the side-effecting Digits property once,
while UnregisterNavigation re-evaluates it inside its loop. If the item count crosses the 10-item
boundary between the two calls, keys are unregistered under different names than they were
registered, leaving orphaned key registrations that later collide. This is the same failure family as
issue #232.
Environment
- OS/version: n/a (logic defect, reproducible wherever QuickFiler runs)
- Python version: n/a
- Command/flags used: n/a
- Data source or fixture:
QuickFiler/Controllers/QfcCollectionController.cs
Steps to Reproduce
- Inspect
RegisterNavigation at QuickFiler/Controllers/QfcCollectionController.cs:1330-1341.
Line 1332 captures the value once: var digits = Digits;, then passes that captured value to
every RegisterNavigationAsyncAction call.
- Inspect
UnregisterNavigation at :1343-1356. Line 1347 re-evaluates Digits inside the
loop, once per iteration.
- Inspect the
Digits property at :114-128. It reads _itemGroups?.Count live and returns 1 for
counts below 10 and 2 at or above 10. It is side-effecting: it also drives
SetVisualDigits via _digitRefreshNeeded.
- Register navigation while the collection holds 10 or more items, so keys are registered as
"01".."09".
- Allow the item count to drop below 10 (for example by filing messages) before
UnregisterNavigation runs.
UnregisterNavigation now computes digits == 1 and attempts to remove "1".."9".
KbdActions.Remove (QuickFiler/Controllers/KbdActions.cs:123-135) returns false silently
when the key is absent; nothing surfaces the mismatch.
- The original
"01".."09" registrations remain. A later Add of the same key collides in
KbdActions.Add (KbdActions.cs:90-98) and throws ArgumentException.
Expected Behavior
The digit width used to unregister a navigation key must be the same width used to register it, so
that every registered key is removed exactly once and no orphan remains.
Actual Behavior
When the count crosses the 10-item boundary between register and unregister, the removal silently
no-ops and the stale registrations persist until a later Add throws ArgumentException.
Logs / Screenshots
Impact / Severity
Produces a delayed, hard-to-attribute ArgumentException in keyboard registration rather than an
immediate failure at the point of the mismatch. Directly analogous to issue #232, which was a
navigation-key collision on page swap.
Source
From: docs/features/potential/2026-08-07-qfc-collection-navigation-digits-desync.md
Summary
QfcCollectionController.RegisterNavigationcaptures the side-effectingDigitsproperty once,while
UnregisterNavigationre-evaluates it inside its loop. If the item count crosses the 10-itemboundary between the two calls, keys are unregistered under different names than they were
registered, leaving orphaned key registrations that later collide. This is the same failure family as
issue #232.
Environment
QuickFiler/Controllers/QfcCollectionController.csSteps to Reproduce
RegisterNavigationatQuickFiler/Controllers/QfcCollectionController.cs:1330-1341.Line 1332 captures the value once:
var digits = Digits;, then passes that captured value toevery
RegisterNavigationAsyncActioncall.UnregisterNavigationat:1343-1356. Line 1347 re-evaluatesDigitsinside theloop, once per iteration.
Digitsproperty at:114-128. It reads_itemGroups?.Countlive and returns 1 forcounts below 10 and 2 at or above 10. It is side-effecting: it also drives
SetVisualDigitsvia_digitRefreshNeeded."01".."09".UnregisterNavigationruns.UnregisterNavigationnow computesdigits == 1and attempts to remove"1".."9".KbdActions.Remove(QuickFiler/Controllers/KbdActions.cs:123-135) returnsfalsesilentlywhen the key is absent; nothing surfaces the mismatch.
"01".."09"registrations remain. A laterAddof the same key collides inKbdActions.Add(KbdActions.cs:90-98) and throwsArgumentException.Expected Behavior
The digit width used to unregister a navigation key must be the same width used to register it, so
that every registered key is removed exactly once and no orphan remains.
Actual Behavior
When the count crosses the 10-item boundary between register and unregister, the removal silently
no-ops and the stale registrations persist until a later
AddthrowsArgumentException.Logs / Screenshots
QfcCollectionController.cs:1332(single capture),:1347(per-iteration re-evaluation),:114-128(the live, side-effectingDigitsproperty), andKbdActions.cs:123-135(the silentfalsereturn). Discovered during preparation research forissue Feature: quickfiler-collection-controller-coverage #454 (epic Feature: quickfiler-80-per-file-coverage #136, child F11); full analysis in
docs/features/active/2026-08-07-quickfiler-collection-controller-coverage-454/research/qfc-collection-controller.mdsection E14.
Impact / Severity
Produces a delayed, hard-to-attribute
ArgumentExceptionin keyboard registration rather than animmediate failure at the point of the mismatch. Directly analogous to issue #232, which was a
navigation-key collision on page swap.
Source
From: docs/features/potential/2026-08-07-qfc-collection-navigation-digits-desync.md