Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/files/FileBrowserPanel.tsx — the root-reveal selection suppression is not scoped to the reveal that captured it. suppressedSelectedPathRef records breadcrumbRevealId (lines 162-165) but consumption (line 343) matches on path only, and nothing clears the entry after the root reveal completes. A stale entry can therefore downgrade a later external reveal of the same file (file picker, content search, chat link) to select-without-scroll, leaving the row off-screen.
Suggested fix: capture selectedPathRevealId instead and require both path and revealId to match at the consumption site.
No other findings: the crumb <button> keeps text-left, hover/focus-visible treatment matches the existing raw breadcrumb-button pattern in ChatHeader.tsx, the suppressed branch now deselects before selecting, and an exhausted root-reveal retry acknowledges the request instead of stranding it.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Scope
apps/web/src/components/files/FileBrowserPanel.tsxapps/web/src/components/files/FilePreviewPanel.tsx
Checks performed
- Traced the three reveal paths (selected-path sync, breadcrumb file/directory reveal, breadcrumb project-root reveal) through effect declaration order at mount and on entry-load, including the pending-entries window.
- Verified one-shot semantics:
handledBreadcrumbRevealRef,capturedRootRevealIdRef, and the parent'shandleBreadcrumbRevealHandled(stableuseCallback, so the rAF retry loop is not restarted by identity churn). Retryable states (entriesQuery.isPending, missing virtualized viewport) are retained rather than acknowledged; the exhausted-retry branch now acknowledges instead of strandingbreadcrumbReveal. - Traced every read/write of
suppressedSelectedPathRef(declaration 147, clear 152, capture 162, clear 313, consume/clear 343-346) — the storedrevealIdis written but never read, and no completion path clears it. - Checked
visibleDirectoryTreePathagainst mid-chain and head-of-chain flattened directories; both resolve to the same deepest row key, and the loop terminates on finite entries. - Compared the new crumb
<button>against the repository's clickable-breadcrumb precedent (ChatHeader.tsx:242,288): raw button withcursor-pointer,rounded-sm,focus-visible:ring-2 focus-visible:ring-ring, explicittext-leftwithtruncate. Confirmed the preview subheader is not an Electron drag region, so no-webkit-app-regionhandling is required.
🔍 Agent activity — 1 tool call
💭 I'm checking the current state of the PR to see if the prior issues have been addressed, particularly the suppression branch logic that was flagged at line 343 in the earlier commit. Looking at the latest commit f05a667 to understand how it addresses the prior feedback about scope root reveal selection sync. The latest commit added a deselect loop and scoped the suppression to a specific path and reveal ID. Let me check the full code around lines 300-500 to see what other issues might still be present. Looking at the remaining issues flagged in prior reviews. The
data-file-tree-virtualized-scrollselector at line 407 was previously noted as potentially never matching, causing the loop to retry for ~120 frames before silently giving up. I can see that's been addressed — line 411 now includes an acknowledgment callbackonBreadcrumbRevealHandled(breadcrumbRevealId)in the else branch, so it's no longer silently failing when the selector isn't found. But the selector itself might still not exist. Let me search through the codebase and node_modules to verify whether@pierre/treesactually defines that attribute. I'm noticing a discrepancy between what git_grep found and what the view displayed — line 369 versus 407, a 38-line offset. It could be that grep ran against a different revision than what was shown, since the default revision behavior depends on the repository context. Looking at the grep results for getSelectedPaths, I'm seeing matches at lines 335, 355, 368, and 369, which aligns with what I'd expect. The real issue is that the attribute I'm looking for doesn't appear anywhere else in the repo — it's coming from the @pierre/trees package in node_modules, which isn't tracked in git. I need to check whether @pierre/trees is vendored or if I should look at the actual package to see if that attribute exists there. The selector coupling issue was already flagged before and the code has since been updated to handle it properly in the else branch, so I don't need to re-flag that. Now I'm checking UI consistency against the instructions around shared components and Tailwind ownership, though the customer's additional rules in the .macroscope file are cutting off mid-thought. I'm walking through the reveal semantics to catch any concrete issues that aren't already covered. When the explorer closes and a user clicks a breadcrumb,setExplorerOpen(true)mounts FileBrowserPanel with the breadcrumbRevealPath set. The entries query stays pending whileentryKindis undefined, so theonBreadcrumbRevealHandledcallback gets held back until the query completes—that's working as intended. Now I need to trace through what happens in the non-root case. The effect dependencies includeentries,entriesQuery.isPending, andentryKinds, so it re-runs when those change. TheresetPathseffect at line 303 runs before the reveal effect at 390 since effects execute in declaration order, which meansmodel.getItemshould be available synchronously after the reset. For non-root reveals, the suppression state only gets set during root reveals, but the selectedPath effect at 310 handles the non-root case by clearing suppression whenbreadcrumbRevealPathis set. The tricky part is the ordering when the explorer opens for a directory breadcrumb—FileBrowserPanel mounts fresh, the selectedPath effect runs first and selects the open file, but the reveal hasn't been handled yet. Then the breadcrumb effect at 390 runs and deselects everything to select the directory row instead, so both effects execute in the same commit and the final scroll to the directory wins. It's a bit inefficient with double scrolling, but the customer's instruction about suppression specifically applies to root reveals. For directory or file breadcrumb reveals, the missing suppression means the selectedPath effect will scroll to the current file first, then the breadcrumb effect scrolls to the directory—since both run synchronously in the same commit, the final state is correct even if there's redundant scrolling. There's a potential issue emerging withvisibleDirectoryTreePathwhen a directory row gets flattened while its sole child is being processed. Let me trace through the git history to understand whenvisibleDirectoryTreePathwas introduced and how the flattening logic evolved. The real concern though is a potential bug in the current version: the suppression mechanism only checks the path when deciding whether to reveal, but it stores therevealIdwithout validating it matches, which could cause issues if the reveal state gets out of sync with the actual selected path. So the suppression object persists after the breadcrumb reveal completes because the cleanup condition at line 151 doesn't trigger — breadcrumbRevealPath becomes null, which fails the!== ""check. Later when the user reopens the same file F from an external source, the selectedPathRevealId increments and the selectedPath effect runs again, but now it finds a matching suppression entry and applies it incorrectly. The real fix is to scope suppression to the specific reveal that triggered it by storing the reveal ID at capture time and requiring a match when the selectedPath effect runs. That way, suppression only applies to the immediate reveal cycle it was meant for, and gets cleared once that reveal completes or a new one starts. The effect execution order confirms the suppression applies correctly on mount since the capture happens before the selectedPath effect runs in the same pass. This is a legitimate regression when the same path gets revealed again later with a new revealId, so it's worth flagging. I should verify whether anything else clears the suppression state once the reveal completes. Looking at the cleanup logic,capturedRootRevealIdRefonly guards the capture itself, and after handling the root reveal,breadcrumbRevealPathgets set to null but nothing clears the suppression. If breadcrumbReveal is never handled, it stays empty. The animation frame cancellation only happens in one branch, so unmounting during retries would still clean up properly. There's also the question of whethercloseSearch()should run during a root reveal—it mutates user state by closing an active tree search, but the customer rule says a project-root reveal should preserve the current selected item, so closing search might actually be the intended behavior. For the breadcrumb crumb itself, whenrevealBreadcrumbtargets the current file, it uses the file path to select and scroll to that item, which works fine. On the UI side, the button now hastext-leftapplied, and while it's a raw<button>rather than a Button primitive, that's acceptable here since it's a breadcrumb crumb with specific truncation geometry that differs from the standard primitive. I should verify whether there's an existing breadcrumb component orButton variant="link"in the ui library, and also check if the focus ring styling (focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring) matches the repo's conventions — some projects use different ring sizes or offsets, so I want to make sure this is consistent. The focus ring styling on the new button differs slightly from the app's convention—it uses a ring without offset instead of the offset variant—but that's just a minor aesthetic difference that doesn't warrant flagging. Now I'm checking whether there's an existingWorkspaceBreadcrumbItemcomponent I could reuse for the file breadcrumb, though the current implementation is based on a ScrollArea pattern that might be distinct. The main issue I'm investigating is whether suppression state becomes stale after the root reveal completes. I'm tracing through the scenario where the panel is already open and the root crumb gets clicked: the breadcrumb reveal ID gets captured into suppression, but the consumption logic only checks the path, not the reveal ID. When the current file has already been revealed, the condition that would trigger a new capture doesn't fire, which is actually the correct behavior here. The real stale case would occur if capture happens but the selectedPath effect never consumes it. Both effects have overlapping dependencies on selectedPath and selectedPathRevealId, so they should run together whenever those change. I'm trying to pinpoint exactly when capture could happen without consumption following in the same commit. If the entry kind isn't a file, the effect returns early without consuming suppression—like when entries are still loading on fresh mount. Once entries load and treePaths updates, the effect re-runs and consumes the suppression, then selects without scroll. The root reveal effect handles scrolling to top once the tree renders. If a file isn't in the entries at all, the effect always returns early and suppression stays captured forever, but that's fine since there's no item to scroll to anyway. When the file is present and consumption happens, suppression gets consumed in the same pass, so the stale case seems rare. But there's an earlier return at line 337 when selectedInTree and treeSelectionPathRef match—that sets handledRevealRef and returns without clearing suppression. On fresh mount treeSelectionPathRef is null, but when the user clicks a row in the tree for file G, treeSelectionPathRef becomes G and onOpenFile triggers, making selectedPath G with a new revealId. The capture effect runs since selectedPath changed, but breadcrumbRevealPath is null at that moment, so the condition at line 151 evaluates differently. The real question is when breadcrumbRevealPath equals an empty string while a new file opens—that happens during root-reveal retries. After root reveal is handled, breadcrumbRevealPath becomes null. If the user opens a different file via chat link during retries, the capture effect runs again, but since breadcrumbRevealPath is empty and capturedRootRevealIdRef already captured that breadcrumbRevealId, it returns without capturing again. So the stale {F,1} suppression from mount—was it actually consumed? Let me trace through the scenario: mount with root reveal and selectedPath=F, capture {F,1}, entries not loaded yet so selectedPath effect early-returns. Before entries load, user opens file G via chat link, so selectedPath=G. The capture effect sees breadcrumbRevealPath still empty and capturedRootRevealIdRef already set, so it returns and suppression stays {F,1}. The selectedPath effect for G early-returns because entryKinds is empty. Then entries load and selectedPath effect runs for G—suppression path F doesn't match G, so it goes to the normal branch and scrolls to G. The suppression {F,1} is still stale. Later when the user re-opens F, the suppression matches F, so it takes the suppressed branch and selects F but doesn't scroll, which could be a problem. This stale scenario requires switching files while root reveal is still pending—it's real but fairly narrow. I've already flagged similar variants twice, so I'm hesitant to flag this again as it risks nitpicking. Let me look for something more fundamental instead. Now I'm checking the root reveal logic against the customer's requirement: the project-root reveal should scroll the tree viewport and preserve the selected item, selecting the current file without focusing or scrolling it initially, then waiting for the virtualized viewport before scrolling to root. The root branch callsrevealProjectRoot()synchronously during the mount commit, and if the container exists and the query matches immediately, it sets scrollTop to 0, which is already the starting position. But there's an ordering issue with howhandledBreadcrumbRevealRefandonBreadcrumbRevealHandled()interact at mount — the breadcrumb reveal gets set to null in the parent synchronously in the same effect pass, causing FileBrowserPanel to re-render with breadcrumbRevealPath as null. This means the capture effect's condition for clearing suppression doesn't trigger correctly if the selectedPath effect has already exited early. The root reveal might succeed immediately at mount when the scroll container is found and scrollTop is 0 with an empty tree, but then when entries load and rows render, the tree stays at the top since the selected path branch is suppressed and doesn't scroll. The question is whether Pierre auto-scrolls to a new selection once the tree populates. Now I'm checking thevisibleDirectoryTreePathfunction — the flattened chain logic that walks through single-child directories. I need to verify it's working correctly, particularly around how it handles the directory traversal. The concern is about what happens when a user clicks a directory crumb that's part of a flattened chain. Ifsrconly containssrc/components, the visible row showssrc/components, and the deepest folder gets selected — that matches VS Code's behavior. But the reverse direction is trickier: what if the clicked directory itself is a child within a flattened chain rather than the head of it? Testing a few scenarios: ifsrchas only childcomponents, andcomponentshas multiple children, clicking thecomponentscrumb returnssrc/components/as the target, which matches the flattened row. Clickingsrcwalks down tocomponents, finds multiple children, and also returnssrc/components/— same row, which is correct. For a directory with a single file child, it returns the directory path without flattening. The pattern holds for a directory whose only child is another directory too. The filtering logic iterates through entries checkingstartsWithon the prefix, which is O(n) per level but acceptable. Empty directories return their path correctly. The algorithm can't loop infinitely sincecurrentPathstrictly deepens and the entries array is finite. The flattening behavior in Pierre applies while directories are collapsed, similar to how VS Code handles compact folders. When ancestors expand and call expand on flattened paths likea/ora/b/, the model might return registered items even if they're not visible rows, but that's harmless. Now I'm looking at a different issue: the root breadcrumb with an empty path — clicking it opens the explorer and scrolls to the top, but I need to check what happens to the selection state. When switching between file previews, thebreadcrumbRevealstate persists across file switches if it hasn't been handled yet, which is the intended behavior. Clicking the same crumb twice works correctly since each click increments the id and triggers a new reveal. When the explorer is closed,revealBreadcrumbopens it first before mounting FileBrowserPanel, so the reveal gets handled properly. But I need to check whether FileBrowserPanel is actually rendered in inline mode — if it's not, then clicking a breadcrumb in inline mode would set the state but never handle it, leaving the reveal stuck in a retryable state indefinitely. The FileBrowserPanel uses a key based on environment and working directory to prevent remounting when files change. Now I'm checking whether scrolling the root back to the top conflicts with the selected-path effect when the panel's already open—specifically when a file has been revealed and the user navigates back to the root. If the file was already handled, the root effect just scrolls to the top without triggering the selected-path effect again, so the file stays selected while the view resets. This is correct. Even in the edge case where a new file opens from a chat link and the user immediately clicks the root crumb, the selected-path effect runs synchronously after commit, so the handled state is set right away—making that unhandled scenario rare outside of initial mount. The breadcrumb buttons are now focusable tab stops in a horizontally scrollable bar, which is expected behavior. The padding adjustment and max-width constraint with border-box sizing means the truncation width is slightly narrower than before. I should check whether the codebase has an established pattern for clickable breadcrumb segments—looking at WorkspaceBreadcrumb and ChatHeader to see if they use a Button component with specific variants or if raw buttons are already used elsewhere. I'm noticing a difference in how the breadcrumb buttons handle focus styling—the new approach usesfocus-visible:outline-noneinstead ofoutline-hidden. In Tailwind v4, these have different accessibility implications:outline-hiddenpreserves a transparent outline for forced-colors mode, whileoutline-noneonly removes the outline style itself, which could affect visibility in high-contrast modes. Looking at the codebase, there's inconsistency across components—some useoutline-noneas a base style with ring focus indicators, while others scope it tofocus-visible:outline-hidden. This is a minor accessibility consideration, though it probably hasn't been flagged in previous reviews. The real question is what subtle issue the final commit introduced. Moving the capture logic from render-time to an effect changes the timing, but since both effects run in the same commit, the ordering should be preserved—unless there's a dependency issue I'm missing. The guard ensures capture happens once per breadcrumb reveal ID, so when the user clicks the root crumb again with a new ID, it recaptures with the updated state. Now I'm looking at the deselect loop that was added in the suppressed branch around line 355. The suppressed branch intentionally skips callingcloseSearch()to avoid mutating the search state, which is fine at mount when there's no active search. And when selecting the item viaselectedItem.select(), the file should be accessible as long as its parent directories are expanded—files are always leaf rows with their own path, so that's not an issue either. I'm wondering if the remaining problem is the stale-suppression case, or something else entirely. Let me trace through the deselect logic more carefully. The new code deselects all currently selected paths before selecting the target item, and this happens after expanding ancestors rather than before—but that shouldn't matter. ThesyncingSelectionRefflag is set before the deselect/select operations and cleared in a microtask, same as the other branch. Now I need to think through what happens whenonSelectionChangefires and triggersdragMention.handleSelectionChange. Actually, let me focus on a more concrete scenario: when the explorer is closed and the user clicks a directory crumb to open it. The panel mounts withbreadcrumbRevealPathset to something like "src/components", and the capture effect runs. ThebreadcrumbRevealPathcheck clears suppression, so theselectedPatheffect isn't suppressed at mount — it selects the first entry and scrolls to it with focus. Then the breadcrumb effect runs, deselecting everything and selecting the directory row instead. The first entry gets deselected, which is fine since the spec says revealing a directory should select the directory itself. There's a potential issue with focus though: if entries are still loading when the directory mounts, theselectedPatheffect early-returns and the breadcrumb effect can't run becauseentryKindis undefined. Once entries load, theselectedPatheffect runs again. The stale suppression flag is the real concern here. It persists even after the reveal completes — nothing clears it except consuming the breadcrumb, settingselectedPathto null, or triggering a non-root breadcrumb reveal. The window where this matters is actually wider than just the in-flight reveal period. Let me trace through a scenario where you close the explorer, then switch files while a root reveal is happening. When entries are still pending, theselectedPatheffect bails early without consuming the suppression. The reveal effect retries viarAF, entries eventually load, the tree renders, andselectedPathre-runs on thetreePathschange — that's when consumption typically happens. But if you changeselectedPathbefore entries arrive (like clicking a chat file link right after opening), you can leave the suppression stale. It's a narrow edge case, but plausible. Now looking at the root-reveal branch itself: thehandledBreadcrumbRevealRefis only set on success. If the retry loop exhausts its 120-frame limit... Examining the retry loop more carefully... The real issue is thatentriesgets recreated as a new array on every render when data is undefined, which causes the breadcrumb effect to re-run constantly. This restarts the retry logic withattemptsRemainingreset to 120 each time, but that's not necessarily fatal since the cleanup just cancels the pending animation frame. OncehandledBreadcrumbRevealRefis set after a successful reveal, the effect returns early anyway. The concern is whether this could create an infinite re-run loop, but it shouldn't since the animation frame is only cancelled and restarted when dependencies actually change. Now I'm checking whetherscrollContainer.scrollTop = 0is sufficient if the virtualizer uses transform-based scrolling instead. And looking at the button styling — it's now inside a flex row with truncate and max-width constraints, whereas previously the span had those properties. The button itself has minimal padding. Buttons are inline-block by default and work fine with truncate, but there's a UA stylesheet quirk: buttons don't inherit font properties like regular elements do, so they get Arial at a smaller size unless explicitly reset. Tailwind's preflight handles this with font-family and font-size inheritance... Actually, looking at Tailwind v4's preflight, it's resetting font, background-color, and appearance for buttons and form elements to normalize them across browsers. The text-align property isn't being reset though, so the UA's default center alignment still applies. Now I'm thinking through whether the file breadcrumb header in the preview panel is within an Electron drag region, since the WorkspaceBreadcrumb has special handling to keep drag regions interactive. The subheader isn't part of the titlebar, so I need to check if drag-region rules apply to it or if it's treated as a separate drag zone. I'm reconsidering whether the FilePreviewPanel breadcrumb bar should be flagged as a violation—it's technically not a drag region since it's the subheader, so there's nothing concrete to report there. But I'm realizing there might actually be something worth flagging with how the directory tree path handles file crumbs when the parent chain gets flattened. When clicking a directory crumb, the code expands all segments of that breadcrumb path, which works fine for directories. But in the flattened case, the visible row might be deeper than the clicked directory—for instance, clickingsrcwhen the actual row issrc/components/. The expand loop only walks the clicked path segments, so it expandssrc/but the visible flattened row atsrc/components/might not get properly expanded. And there's also the reverse problem where the visible row could be shallower than the clicked path. Testing the chain scenario: if Pierre rendersa/b/cas a single flattened row and the user clicks crumbb, the code resolves down to the same visible row correctly. Clickingaalso resolves down toa/b/c/. So walking downward handles mid-chain clicks. The real failure mode would only occur if Pierre keys flattened rows by the topmost segment rather than the deepest, but the author's comment asserts it uses the deepest. There's still a genuine gap though: the walk-down logic assumes the clicked directory is itself part of a flattened chain continuing downward, which may not always hold. I've found one plausible issue—stale suppression not cleared when the root reveal completes, causing a later re-open of the same file to skip scrolling. Let me verify the consumption condition once more for a realistic case. The suppression check happens inside the selectedPath effect only after passing several guards: selectedPath must be truthy, the handled check must pass, the entry can't be a file, the item must exist, and the selectedInTree/treeSelectionPathRef match must fail. Testing a concrete scenario: mounting via root crumb with cached entries immediately available. The file F gets captured in the effect at 150, then when the selectedPath effect runs at 310, entryKinds already has F registered as a file, the tree model was just created with an empty paths array but resetPaths runs first at 303 to register items, getItem(F) succeeds, selectedInTree is false, and the suppression matches—so it gets consumed. That works. So stale suppression really does require both pending entries and a file switch, which is narrow. But there might be another path where suppression gets captured while later becoming unwanted—like when a file is already open and the explorer... Looking at how StrictMode double-invokes effects in dev mode: the capture effect runs twice, setting the ref on first run and returning early on second run since the ref already matches. The selectedPath effect similarly consumes suppression on first run and returns early on second run when the handled state matches. The breadcrumb root effect follows the same pattern—first run schedules and succeeds, second run... Now I'm noticing something about the root branch: whenscrollContaineris found, they callmodel.closeSearch(), but the customer's rule states reveal requests should "resolve and validate the visible target without mutating search, focus, or selection"—meaning these should be treated as one-shot events that don't alter state. Closing search does make sense though, especially if a tree search is active with "hide-non-matches" filtering the visible rows. Scrolling to the top of a filtered list wouldn't reach the actual project root, so closing search first restores all rows. The other branches also callmodel.closeSearch(), so it's consistent. But there's a timing issue: in the root branch,model.closeSearch()is called and thenscrollTopis set to 0 in the same tick. Closing search likely triggers an async re-render of the web component to restore the full row list, so settingscrollTopbefore that re-projection completes could scroll to the wrong position. The other branches usemodel.scrollToPath(...)instead, which presumably handles the scroll after the tree re-renders. This could be a real bug where the synchronous scroll gets overwritten, though I'm not certain about the virtualizer's internals. Let me look at the FilePreviewPanel next. Now I'm checking the state handling in the breadcrumb reveal logic —revealBreadcrumbisn't memoized but doesn't read anything stale, andhandleBreadcrumbRevealHandledis wrapped inuseCallbackwith an empty dependency array, so it stays stable and won't cause the effect to re-run unnecessarily. That's good. But there's a potential issue with howbreadcrumbRevealIdis tracked — when the path is null, the id defaults to 0, and the child effect depends on this id. If a root reveal completes and then the user clicks a different crumb, the id changes, but I need to trace through what happens in that scenario. The refcapturedRootRevealIdRefstarts as null, and with the initial path being null and id being 0, the effect returns early, which is fine. Now I want to verify the customer's specific requirement about validating intermediate directory actions against a real flattened chain — when expanding ancestors for a file target, the code walks throughsegments.slice(0, -1)and fetches each ancestor usingmodel.getItem(). If ancestors are flattened (likea/b/c/file.tswherea→bcollapses into a single rowa/b), the item fora/might not be rendered as a separate row, but sincemodel.getItem()registers all paths passed toresetPaths(includinga/), it should still return an item even if not visible. Calling.expand()on it is either harmless or actually necessary, and since the pre-existing code at line 379 does exactly this for selected-path reveal and works fine, this behavior is consistent. I don't have a concrete new violation to report, though the instruction notes that prior checks defaulted to failure and found issues before. ef at consumption. This is a concrete regression risk that fits the pattern the check is designed to catch — reveal state that doesn't stay one-shot-scoped but instead leaks across component lifecycles. I'll add an inline comment on the capture block explaining the fix: store theselectedPathRevealIdalongside the suppression flag and validate both match when the suppression is consumed. the suppression correctly persists across slow-loading entries and resets when switching files or reopening the same file with a new revealRequestId. Now I need to verify that revealRequestId increments on each open request, including re-opens of the same file—the prop documentation confirms it's bumped when a path should be revealed again. For the comment placement, I'm looking at the effect block around lines 150-167 in the diff, and the line number needs to correspond to the RIGHT side of the diff for the GitHub PR review. The most informative anchor is lines 162-165, which capture the stored revealId. The core issue is thatsuppressedSelectedPathRefstoresbreadcrumbRevealIdbut the consumption at line 343 only compares the path, leaving no mechanism to clear the suppression once the root reveal finishes. If the selected file changes before the tree consumes the entry during a mount-time root reveal, the suppression persists; when that same file is revealed again externally (via file picker, search, or chat link), it takes the suppressed branch and skips the scroll, leaving the file off-screen. The fix is to storeselectedPathRevealIdat capture and require both path and revealId to match at consumption. I should also check line 152's asymmetric clearing as part of the same pattern. Given the consistency risk here, I'll mark this as a failure with a finding rather than a warning. Let me verify thesuppressedSelectedusage one more time — I've found it at lines 147, 152, 162, 313, and 343/346, which covers declaration, clearing, setting, and consumption. The key issue is that the consumption at line 343 ignores therevealId, which confirms the asymmetry. I'll post the comment now.
1. modify_pr — One finding on the reveal-state scoping in FileBrowserPanel. Everything else in this revision (the deselect before select() in the suppressed branch, the text-left crumb button, acknowledging an exhausted root-reveal retry) looks right.
Agent Credits: 207 credits