diff --git a/neode-ui/src/views/Apps.vue b/neode-ui/src/views/Apps.vue
index 1993cfc6..dd4a2838 100644
--- a/neode-ui/src/views/Apps.vue
+++ b/neode-ui/src/views/Apps.vue
@@ -380,7 +380,7 @@ let appsAnimationDone = false
diff --git a/neode-ui/src/views/dashboard/keepAliveRoutes.ts b/neode-ui/src/views/dashboard/keepAliveRoutes.ts
index ea3d81c7..67825299 100644
--- a/neode-ui/src/views/dashboard/keepAliveRoutes.ts
+++ b/neode-ui/src/views/dashboard/keepAliveRoutes.ts
@@ -8,21 +8,58 @@
// `server/openwrt`, `web5/credentials`, `goals/:goalId` and `app-session/:appId`.
import type { RouteLocationNormalizedLoaded } from 'vue-router'
+import { TAB_ORDER } from './useRouteTransitions'
/** Maximum number of main-tab component instances kept alive at once (D-03).
* Bounds memory on low-power fleet nodes — the oldest unused instance evicts
- * once this cap is exceeded (Vue's own LRU eviction). */
+ * once this cap is exceeded (Vue's own LRU eviction). Stays at 6 against the
+ * ~11 registered paths below (FA-D) — plan 02-08 tunes it against real
+ * on-device memory, not guessed at again here. */
export const KEEP_ALIVE_MAX = 6
-/** Paths whose component instance survives a tab switch (D-01).
+/** TAB_ORDER paths deliberately withheld from the instance cache this plan
+ * (02-04) would otherwise register. Two distinct reasons populate this set —
+ * kept in one place so `KEEP_ALIVE_PATHS` stays a single derivation:
*
- * Seeded with only the tracer tab (Marketplace) for this plan — plan 02-04
- * widens this set after auditing every main tab's mount/activate lifecycle.
- * Do not add paths here without doing that audit first: a view with an
- * un-audited side effect in `onMounted` that should have moved to
- * `onActivated`/`onDeactivated` would misbehave the moment it's kept alive. */
+ * 1. Measured `already fast` in 02-FINDINGS.md with `Remounted: false` — no
+ * instance cache to gain (D-02). (None as of 02-04: every TAB_ORDER path
+ * measured `Remounted: true` (Home, Apps, Marketplace, Cloud, Web5,
+ * Fleet) or was `unmeasured` (Mesh, Chat) — neither unmeasured row has a
+ * `Remounted: false`, so per the plan's literal exclusion rule both stay
+ * registered, and their lifecycle was audited in 02-04's Task 1.)
+ * 2. Unaudited-risk (02-04 Rule-3 deviation, not in 02-FINDINGS.md at all):
+ * `/dashboard/settings` is in TAB_ORDER but neither Settings.vue nor any
+ * of its child sections are in this plan's Task 1/2 file lists. A grep
+ * across `neode-ui/src/views/settings/*.vue` found real un-audited side
+ * effects that would misbehave under KeepAlive exactly as this plan
+ * warns against: `SystemDangerZone.vue`'s reboot poll/elapsed intervals,
+ * and one-shot `onMounted`-only fetches in `VpnStatusSection.vue`,
+ * `KioskDisplaySection.vue`, `TransportPrefsCard.vue` and
+ * `ClaudeAuthSection.vue` that would never refresh again once cached.
+ * Registering Settings without that audit would ship the exact
+ * off-screen-timer/stale-data bug this plan exists to prevent — excluded
+ * until a future plan audits it the way Task 1 did for Home/Web5/Chat/
+ * Cloud/Server/Mesh. */
+const WITHHELD_FROM_CACHE: ReadonlySet = new Set([
+ '/dashboard/settings',
+])
+
+/** Paths whose component instance survives a tab switch (D-01), derived from
+ * the single-sourced TAB_ORDER main-tab list (plus `/dashboard/discover`,
+ * the App Store's second tab, measured `remount storm` in 02-FINDINGS.md but
+ * not part of TAB_ORDER) rather than restating every path literally — a
+ * future tab addition to TAB_ORDER is registered automatically instead of
+ * silently missing registration.
+ *
+ * 02-04 widened this from the 02-02 tracer's single seed path after
+ * auditing every main tab's mount/activate/deactivate lifecycle (see
+ * 02-04-SUMMARY.md's per-view side-effect table) — every registered path's
+ * timers, subscriptions and window listeners are now placed for an
+ * activate/deactivate lifecycle, so it's safe for their instances to
+ * survive a tab switch. */
export const KEEP_ALIVE_PATHS: ReadonlySet = new Set([
- '/dashboard/marketplace',
+ ...TAB_ORDER.filter((path) => !WITHHELD_FROM_CACHE.has(path)),
+ '/dashboard/discover',
])
/** True only for an exact path match against KEEP_ALIVE_PATHS. */