Skip to content

fix(quickfiler): navigate expanded subfolders with arrow keys (#1007) - #1010

Merged
drmoisan merged 10 commits into
mainfrom
bug/efc-arrow-keys-skip-subfolders-1007
Oct 26, 2026
Merged

drmoisan merged 10 commits into
mainfrom
bug/efc-arrow-keys-skip-subfolders-1007

Conversation

@drmoisan

Copy link
Copy Markdown
Owner

Summary

  • The arrow keys in the EFC viewer folder tree and in the QFC item-viewer folder list now move through every visible row in tree order, including expanded subfolders at any depth. Before this change, keyboard navigation reached only the top-level rows, so keyboard-driven filing could not reach a subfolder in either viewer.
  • The keyboard rules follow the maintainer-confirmed acceptance criteria AC1 to AC14 in spec.md (four grill-me rounds, confirmed on 2026-10-23). They cover Up and Down in tree order, no wrap at either end, select-on-move in EFC, the pending-highlight model in the open QFC list, standard tree Right and Left, Ctrl+Left parent selection, segment double-click, closed-list QFC behaviour, and Home, End, PageUp and PageDown.
  • AC14 fixes a related defect: an expand affordance [+] could appear on a folder with no subfolders. OutlookFolderHierarchyProvider.MapNode now derives has-children from the children that resolve in the folder snapshot.
  • A re-entrancy guard on the EFC row-leaf expansion stops a held Right key from leaving the row collapsed.
  • Removed behaviour for keyboard users: the Bug: breadcrumb-left-right-arrow-parent-child-navigation #440 Left parent-segment walk, the EFC Left segment hiding, and the [+] restore of trailing segments.
  • Toolchain and coverage gates pass after the merge of origin/main. Two feature reviews were run, and the re-audit passed. Manual Outlook verification is still pending.

Why

The root cause analysis in spec.md records these causes:

  1. EFC Up and Down walked only top-level rows. HandleUpArrow and MoveSelection in QuickFiler/Controllers/BreadcrumbBridgeRouter.Arrows.cs used FindSelectable and IndexOf, which iterate _rows only. They never read the Bug: efc-qfc-folder-tree-selection-and-nesting #1002 descendant tree. SelectRow called TrySelectDescendant(null), so Down from a selected subfolder cleared it and jumped to the next row.
  2. QFC navigation walked only rows and acted on the committed row. BreadcrumbSelectionSession.Move tracked pending state by row identity only. QFC Left and Right went through FolderBreadcrumbBridgeRouter.ArrowAsync against the committed row, not the pending highlight. The page-side onArrow pre-filter in FolderBreadcrumb.js also read the committed row.
  3. The gap was a recorded scope exclusion of Bug: efc-qfc-folder-tree-selection-and-nesting #1002 and Bug: QuickFiler descendant selection and recursive expansion #997, not a regression. The existing Bug: breadcrumb-left-right-arrow-parent-child-navigation #440 arrow transitions conflicted with the confirmed tree semantics (D-Q4, D-Q9).
  4. [+] without subfolders (AC14). MapNode set HasChildren from node.ChildKeys.Count > 0. A child key whose folder was absent from the snapshot therefore produced [+], a fetch returned zero children, and the expanded state showed [−] with nothing below it.
  5. Re-entrant Right on the EFC row leaf. ExpandLeafAsync ended with a toggle and had no guard. Two overlapping fetches (for example from key auto-repeat) each toggled, and the row ended collapsed.
  6. Secondary blockers. These were: QFC GetSelectedFolder ignored the active segment; a single click on a segment in the open QFC list bubbled to the row commit; the ReExpand restore replaced the trailing segments instead of listing the active segment's children; and a collapse could leave the selection on a hidden node.

What Changed

Shared visible-order helper (UtilitiesCS)

  • New pure static BreadcrumbVisibleOrder with an immutable BreadcrumbVisiblePosition. It flattens rows and expanded descendants in pre-order and provides IndexOf, Step (clamped, no wrap), First and Last. Both viewers use it: EFC through BreadcrumbRow.AppendVisiblePositions, and QFC through BreadcrumbStateModel.VisiblePositions().

EFC router and page script

  • New partial QuickFiler/Controllers/BreadcrumbBridgeRouter.Navigation.cs. Moving onto a node commits through SelectHierarchyPath (the mouse-click path, AC3). Moving onto a row entry goes through the new SelectRowEntry.
  • New key arms: ParentLeft (Ctrl+Left), Home, End, PageUp and PageDown. With nothing selected, Down and Home select the first row, and Up and End select the last.
  • Right and Left were rewritten to follow the decision table in spec.md. TryRightTreeTransitionAsync and the Bug: breadcrumb-left-right-arrow-parent-child-navigation #440 Left walk were removed.
  • The SegmentDoubleClick arm is now atomic: it activates the segment, commits it and shortens the row.
  • A collapse that hides the selected node re-commits the nearest visible ancestor. This narrows the Bug: efc-qfc-folder-tree-selection-and-nesting #1002 invariant I3 (decision S1).
  • BridgeJs in BreadcrumbDocumentAssets.cs maps the Ctrl and Home/End/Page keys and posts a measured pageSize. It also scrolls to the selected .child when one is present.

QFC selection session, router, coordinator and page script

  • New partial BreadcrumbSelectionSession.Tree.cs. It adds PendingNode, the open-list visible-order walk, boundary and page moves, the Ctrl+Left parent move, and a row-shape snapshot that Cancel restores (D-Q24). CommitPending selects the pending descendant.
  • New partial FolderBreadcrumbBridgeRouter.Navigation.cs: when the list is open, Left and Right act on the pending entry.
  • New partial QuickFiler/Viewers/BreadcrumbBridgeCoordinator.Keyboard.cs with these rules:
    • Right with the list closed opens the list. Left with the list closed is consumed.
    • Left on a non-expanded entry in the open list commits and closes the list.
    • Right on a leaf in the open list cancels, then raises UnhandledArrow(Right), which opens the pop-out dialog.
    • The coordinator also handles the new selectorSegmentDoubleClick message.
  • BreadcrumbSelectionMap.SelectedRowValue makes GetSelectedFolder return the folder the shortened row displays.
  • Model and projection: BreadcrumbStateModel.LeftArrow and RightArrow lose the Bug: breadcrumb-left-right-arrow-parent-child-navigation #440 walks. A shortened row expands its active segment. BreadcrumbRenderProjection emits Minus at the shortened terminal while the row is expanded.
  • FolderBreadcrumb.js:
    • Adds Home, End, PageUp and PageDown (with pageSize), and Ctrl+Left as the parent selector key.
    • onArrow always posts arrowKey, and C# decides the outcome (S6).
    • Marks the pending subfolder with .sub.active.
    • A single click on a non-leaf segment in the open list moves only the pending highlight.
  • FolderBreadcrumb.html gains the .sub.active style, so the pending subfolder highlight is visible. This was review finding B-1.

AC14: no [+] on a folder without subfolders

  • OutlookFolderHierarchyProvider.MapNode derives HasChildren from snapshot.GetChildren(node.Key).Count > 0.
  • As a second layer of protection, the EFC renderer, the QFC projection and makeSubfolders show no expand or collapse affordance after a fetch that attached zero children.

Re-entrancy guard

  • The EFC row-leaf expansion is now an idempotent expand-to-true and is guarded against overlapping fetches (S7). A held Right key ends with the row expanded and makes one provider query.

Tests

  • New C# test files (MSTest, Moq, FluentAssertions):
    • BreadcrumbVisibleOrderTests
    • BreadcrumbSelectionSessionTreeIssue1007Tests
    • FolderBreadcrumbBridgeRouterIssue1007Tests (with an Unanchored partial)
    • OutlookFolderHierarchyProviderIssue1007Tests
    • BreadcrumbEmptyExpanderIssue1007Tests
    • BreadcrumbBridgeJsKeysIssue1007Tests
    • BreadcrumbBridgeRouterIssue1007Tests (with Navigation and DoubleClick partials)
    • BreadcrumbBridgeCoordinatorIssue1007Tests (with an Unhandled partial)
    • FolderBreadcrumbKeysIssue1007Tests
  • New node suite tests/quickfiler-breadcrumb/issue-1007-keys.test.mjs, plus getBoundingClientRect stubs in the browser harness.
  • Existing tests that pinned the removed Bug: breadcrumb-left-right-arrow-parent-child-navigation #440 Left walk, the Left segment hiding and the [+] restore were rewritten or renamed. The reasons are recorded in evidence/other/test-renames.2026-10-25T23-59.md.

Docs

  • The feature folder docs/features/active/2026-10-22-efc-arrow-keys-skip-subfolders-1007/ holds the issue, spec, research, plan, evidence, feature-review artifacts and the manual Outlook runbook.

Architecture / How It Fits Together

  • EFC:
    1. A keydown in BridgeJs posts arrowKey { rowId, key, pageSize? }.
    2. BreadcrumbMessageCodec decodes it, and BreadcrumbBridgeRouter dispatches it to the Navigation partial.
    3. The Navigation partial computes the visible order with BreadcrumbVisibleOrder, locates the current position, steps, and commits through SelectHierarchyPath or SelectRowEntry.
    4. The full list renders, and the page scrolls to the selected element.
  • QFC, list open:
    1. FolderBreadcrumb.js posts selectorKey { key, pageSize? }.
    2. BreadcrumbBridgeCoordinator passes it to BreadcrumbSelectionSession, which walks the visible order. Only the pending entry changes.
    3. selectorView returns to the page with pendingNodeIdentity, and the page highlights that subfolder.
    4. Enter commits. Escape restores the original choice and the row shape.
  • QFC, Left and Right: these always post arrowKey. The Keyboard partial handles the open/closed and leaf cases and forwards the rest to the pending-aware Navigation partial of FolderBreadcrumbBridgeRouter.
  • Unchanged: the Bug: efc-store-root-selection-leaks-full-outlook-path-into-filing-boundary #614 archive-root rules apply because every EFC commit goes through SelectHierarchyPath. Ctrl+Left never selects the archive root.

Verification

Completed

The final toolchain pass ran after the cycle 1 remediation. The branch already contained the origin/main merge (8bdd970ee) at that point.

Step Command Result
Format dotnet tool run csharpier check . exit 0, Checked 1700 files
Analyzers msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true exit 0, 0 warnings, 0 errors
Type check msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true exit 0, 0 warnings, 0 errors
Tests scripts/vscode/Invoke-MSTestWithCoverage.ps1 7761/7761 passed, 0 failed
Coverage first-party summary lines 58427/67553 (86.49%), branches 14440/17860 (80.85%)
Node suites four tests/quickfiler-breadcrumb suites, normal and reversed order 90/90 passed in each order
  • Regression tests first (AC12). Each behaviour group has a fail-before and a pass-after projection under evidence/regression-testing/. These cover the visible order, EFC vertical, EFC horizontal, AC14, the QFC open and closed lists, QFC clicks, the page scripts, and the QFC pending style.
  • Feature reviews. Two reviews were run.
    • The first review required remediation. Its blocking finding B-1 was that the QFC pending subfolder highlight had no CSS rule, so the highlight was not visible. Commit e804f5d6b fixed it.
    • The same remediation cycle added direct tests for the N-1 paths and renamed the tests flagged as N-2.
    • The cycle 1 re-audit passed.

Recommended

  • Run the manual Outlook verification in docs/features/active/2026-10-22-efc-arrow-keys-skip-subfolders-1007/runbooks/manual-outlook-keyboard-verification.runbook.md. It has not been run in this PR.
  • CI does not run the node suites. To check them locally, run node --test tests/quickfiler-breadcrumb/.

Backward Compatibility / Migration Notes

  • Behaviour removed for keyboard users:

    Parent selection is now Ctrl+Left.

  • The bridge-channel segmentDoubleClick arm in the QFC router is now a logged no-op. QFC double-click moved to the selector channel (selectorSegmentDoubleClick).

  • The message types gain optional constructor parameters with defaults (pageSize, pendingNodeIdentity), so existing call sites compile unchanged.

  • No settings, configuration or persisted-data changes. No migration is needed.

Risks and Mitigations

  • Keyboard behaviour change in the main filing workflow. The maintainer confirmed the rules. Mitigations: tests per AC, and the manual Outlook runbook before merge.
  • Page-script and C# contract drift. The node suites cover the page scripts, and C# string-contract tests (BreadcrumbBridgeJsKeysIssue1007Tests, FolderBreadcrumbKeysIssue1007Tests) pin the script contract inside the CI-run MSTest suite. CI has no node step.
  • Rollback. There is no feature flag. Reverting the merge restores the previous behaviour, with no data migration.

Review Guide

  1. spec.md: the decision table for Right and Left, and AC1 to AC14.
  2. UtilitiesCS/OutlookObjects/Folder/BreadcrumbVisibleOrder.cs and its tests.
  3. EFC: QuickFiler/Controllers/BreadcrumbBridgeRouter.Navigation.cs, then .Arrows.cs, .Selection.cs, .Descendants.cs, and BreadcrumbRow.
  4. QFC: BreadcrumbSelectionSession.Tree.cs, FolderBreadcrumbBridgeRouter.Navigation.cs, BreadcrumbBridgeCoordinator.Keyboard.cs, BreadcrumbStateModel, and BreadcrumbRenderProjection.
  5. AC14: OutlookFolderHierarchyProvider.MapNode, the renderer and projection protection, and their tests.
  6. Page scripts: BridgeJs, FolderBreadcrumb.js, FolderBreadcrumb.html, and the node suite.

About 153 files changed. Most of the line count is feature-folder documentation and evidence under docs/features/active/2026-10-22-efc-arrow-keys-skip-subfolders-1007/. Merge commit 8bdd970ee only brings in origin/main.

Follow-ups

  • Run the manual Outlook keyboard verification runbook and record the result. It is pending.

GitHub Auto-close

🤖 Generated with Claude Code

https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs

drmoisan and others added 10 commits October 22, 2026 13:04
…e bug folder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ASyXWyEc23fGahFNciw3P
Commit the confirmed grill-me transcript (rounds 1 to 3) for the EFC/QFC
arrow-key subfolder navigation bug and record its eleven behaviour rules,
plus the regression-test and toolchain gates, as acceptance criteria in
issue.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
Add the task-researcher artifact for the EFC/QFC arrow-key subfolder
navigation bug, record the maintainer's round 4 answers (Q23 to Q29) in
the grill transcript, and amend the acceptance criteria in issue.md:
AC5 drops the restore-full-path clause, AC8 to AC11 gain the round 4
clauses, and AC14 adds the invariant that no expand affordance is shown
on a folder without subfolders, with the root cause in
OutlookFolderHierarchyProvider.MapNode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
Replace the spec template with the full-bug spec for issue #1007: root
cause, design, message contracts, test strategy with failing tests
first, test-disposition table for the removed #440 Left walk and the
[+] restore, and AC1 to AC14 copied from issue.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
Add the 118-task atomic plan for the EFC/QFC arrow-key subfolder
navigation bug, with failing regression tests first for each behaviour
group, the spec amendments the planner made (selectorHighlight message,
per-file coverage source, disposition-table rows), and the record of the
four in-place preflight fixes. Preflight returned ALL CLEAR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
- Move Up and Down through expanded subfolders in EFC and QFC viewers
- Redefine Left, Right and Ctrl+Left; handle Home, End, PageUp, PageDown
- Highlight the open subfolder in the QFC list
- Change segment double-click; [+] lists that segment's subfolders
- Hide [+] on folders without subfolders in MapNode
- Guard against re-entrant Right while a fetch is in flight
- Add regression tests and issue 1007 evidence

Refs: #1007

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
Bring in #1005 (PR #1008, parallel test isolation) before the #1007
feature review. The two csproj files auto-merged without conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
Add the policy audit, code review, feature audit and remediation inputs
from the first review of #1007 (one blocking finding, B-1: the QFC
pending highlight on a subfolder has no stylesheet rule), the
reviewer's AC check-offs, and the post-merge toolchain evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
- Apply the selected style to .sub.active in FolderBreadcrumb.html and
  add a string-contract test for the rule (review finding B-1)
- Add four tests for router, coordinator and state-model paths that
  had lost direct coverage (N-1)
- Rename six #440-era tests to match the current arrow-key behavior (N-2)
- Check off AC4 again in issue.md and spec.md, and add the remediation
  plan and cycle 1 evidence

Refs: #1007

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
Add the policy audit, code review and feature audit from the cycle 1
re-audit of #1007 (no blocking findings) and the human runbook for the
manual Outlook keyboard verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j43rEpkM36uCjKGmGSwWs
@drmoisan
drmoisan merged commit 6dad068 into main Oct 26, 2026
7 checks passed
@drmoisan
drmoisan deleted the bug/efc-arrow-keys-skip-subfolders-1007 branch October 26, 2026 03:04
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: efc-arrow-keys-skip-subfolders

1 participant