Background
Surfaced in the Phase 5 audit PR review and deferred from that PR as a UX issue, not a spec-conformance gap.
HistoryScreen.tsx derives availableMethods from the live entries array and passes it to HistoryControls. The selected methodFilter is held in screen-local useState, independent of availableMethods.
When entries shrinks — most obviously after the user clicks Clear All — a previously-selected methodFilter can become absent from the new availableMethods set. The filter state is not reset, so the entry list shows zero results with no visible indication that the now-orphaned filter is the cause. The user has to manually clear the Select to recover.
Repro
- Generate some history (call any tool from the inspector).
- In the History screen, set the method filter to e.g.
tools/call.
- Click Clear All.
- Trigger a request that produces history of a different method (e.g.
resources/read).
- Notice the panel shows zero results because the stale
tools/call filter still applies, even though tools/call is no longer in the dropdown.
Suggested fix
Three reasonable approaches; each has tradeoffs:
Option A — eager reset via effect. Add a useEffect in HistoryScreen that resets methodFilter to undefined when the current value falls out of availableMethods:
useEffect(() => {
if (methodFilter && !availableMethods.includes(methodFilter)) {
setMethodFilter(undefined);
}
}, [availableMethods, methodFilter]);
Tradeoff: this is a state-update-in-effect, which the project's lint config disallows via react-hooks/set-state-in-effect (see InspectorView for the same constraint). Would need a refactor or lint suppression.
Option B — derive effective filter at render. Treat methodFilter as user intent that's preserved across transient empty states; compute an effectiveFilter at render that's undefined whenever the value isn't currently in availableMethods:
const effectiveFilter =
methodFilter && availableMethods.includes(methodFilter) ? methodFilter : undefined;
Tradeoff: the HistoryControls Select still shows the stale value as selected (because it receives methodFilter, not effectiveFilter), so the displayed filter and the applied filter can diverge — confusing in its own way. Could pass effectiveFilter to the Select too, which would visually clear the dropdown but also discard the user's stored intent if entries grow back.
Option C — handler-driven reset. Reset methodFilter in the onClearAll handler (and any other handler that intentionally drops entries). Doesn't solve the general case (e.g. ephemeral entry expiration), but covers the obvious user-triggered path without effects or render-time logic.
Recommend C for the simplest correct fix, with B as a defensive backstop if other shrink paths emerge.
Out of scope
Background
Surfaced in the Phase 5 audit PR review and deferred from that PR as a UX issue, not a spec-conformance gap.
HistoryScreen.tsxderivesavailableMethodsfrom the liveentriesarray and passes it toHistoryControls. The selectedmethodFilteris held in screen-localuseState, independent ofavailableMethods.When
entriesshrinks — most obviously after the user clicks Clear All — a previously-selectedmethodFiltercan become absent from the newavailableMethodsset. The filter state is not reset, so the entry list shows zero results with no visible indication that the now-orphaned filter is the cause. The user has to manually clear the Select to recover.Repro
tools/call.resources/read).tools/callfilter still applies, even thoughtools/callis no longer in the dropdown.Suggested fix
Three reasonable approaches; each has tradeoffs:
Option A — eager reset via effect. Add a
useEffectinHistoryScreenthat resetsmethodFiltertoundefinedwhen the current value falls out ofavailableMethods:Tradeoff: this is a state-update-in-effect, which the project's lint config disallows via
react-hooks/set-state-in-effect(seeInspectorViewfor the same constraint). Would need a refactor or lint suppression.Option B — derive effective filter at render. Treat
methodFilteras user intent that's preserved across transient empty states; compute aneffectiveFilterat render that'sundefinedwhenever the value isn't currently inavailableMethods:Tradeoff: the
HistoryControlsSelect still shows the stale value as selected (because it receivesmethodFilter, noteffectiveFilter), so the displayed filter and the applied filter can diverge — confusing in its own way. Could passeffectiveFilterto the Select too, which would visually clear the dropdown but also discard the user's stored intent if entries grow back.Option C — handler-driven reset. Reset
methodFilterin theonClearAllhandler (and any other handler that intentionally drops entries). Doesn't solve the general case (e.g. ephemeral entry expiration), but covers the obvious user-triggered path without effects or render-time logic.Recommend C for the simplest correct fix, with B as a defensive backstop if other shrink paths emerge.
Out of scope
MessageMethod(just landed in Phase 5 audit: annotate spec, close HistoryControls partial #1236).