Follow-up hardening on the RuntimeFallbackBadge viewport-gating work:
- AgentsView.tsx: registerAgentCardRef now returns a cached, stable callback per
key (agentCardRefCallbacksRef) instead of a fresh closure each render. A fresh
closure reads as unmount+remount to React; in environments without
IntersectionObserver the mount path calls setVisibleAgentCardKeys -> re-render
-> another fresh closure -> an infinite re-render loop (including jsdom). The
cached entry is evicted on true unmount (el === null) so the Map cannot grow
unbounded across created/deleted agents.
- ActiveAgentsPanel.tsx: document the viewport-gated badge polling with an FNXC
comment (behavior unchanged).
- useRuntimeFallbackStatus.ts: guard __resetRuntimeFallbackToastDedupeStoreForTests
to a no-op outside the test build (import.meta.env.MODE !== "test") so the
test-only dedupe reset can never affect production code paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes silent runtime fallback visibility (dashboard never read wasConfigured
or session:runtime-resolved) and threads the real FallbackReason
(not_found vs factory_error) through resolveRuntime()/logRuntimeFallback
instead of hardcoding "not_found" for every fallback.
- packages/engine/src/runtime-resolution.ts: resolvePluginRuntime() now
returns a tagged miss result distinguishing not_found from factory_error;
resolveRuntime() threads the real reason through and returns it as
ResolvedRuntime.fallbackReason
- packages/engine/src/agent-session-helpers.ts: includes fallbackReason in
the session:runtime-resolved audit event metadata
- packages/dashboard/src/routes/register-task-workflow-routes.ts: new
GET /api/tasks/:id/runtime-fallback endpoint
- packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts +
packages/dashboard/app/components/RuntimeFallbackBadge.tsx: new polling
hook + badge/toast component wired into TaskCard, ActiveAgentsPanel, and
AgentsView
Ref: Fusion task FUX-022, investigations/FUX-017-hermes-runtime-fallback.md
recommendation #1