3 Commits

Author SHA1 Message Date
archipelago
3ee20430b0 docs(02-08): confirm KEEP_ALIVE_MAX=6 against on-device memory (FA-D)
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m18s
Deployed this phase's frontend + AIUI to archi-dev-box (the dev pair;
x250-dev is currently offline via Tailscale, recorded rather than
silently skipped) via scripts/deploy-to-target.sh --frontend-only.
Verified the served bundle (not just local dist) contains this phase's
changes.

Then took a real on-device measurement instead of leaving the FA-D
KEEP_ALIVE_MAX estimate unexamined: a headless Chromium session on
archi-dev-box cycled all 11 main tabs through 4 full round-trips (44
navigations), reading the JS heap via CDP before/after each cycle.
Memory fluctuated 10-21MB with no monotonic growth across cycles that
each exceed the cap 10 distinct registered paths against KEEP_ALIVE_MAX=6.
Left the constant at 6, now backed by a recorded measurement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:19:50 -04:00
archipelago
03a3e4e0c1 feat(02-04): widen KEEP_ALIVE_PATHS to the full audited main-tab set
All checks were successful
Demo images / Build & push demo images (push) Successful in 3m49s
Task 2 of 02-04 — finishes the remaining tabs' lifecycle audit (Apps.vue,
Discover.vue) and widens registration from the 02-02 tracer's single seed
path to every main tab the profiling pass showed remounting.

- Apps.vue: the 15s "unable to connect" timer follows activate/deactivate
  (idempotent re-arm, cleared on exit) and resets connectionError on entry so
  a since-reconnected node doesn't show a stale error instantly; the intro
  flag stays once-per-session.
- Discover.vue: loadCommunityMarketplace/loadBitcoinPruneStatus now route
  through the same shared 'app-catalog'/'bitcoin.prune-status' cache keys
  Marketplace.vue introduced in 02-02, rather than duplicating the fetch;
  RefreshIndicator wired to the catalog resource's loadState. Discover's own
  dynamic-catalog-first fetcher (fetchAppCatalog with a curated-list
  fallback) is preserved as this key's fetcher for this view — both views
  are valid producers of the same shared cache entry.
- Fleet.vue: confirmed no lifecycle side effects (grep for the five tokens
  found none) — left unchanged, registered as-is.
- keepAliveRoutes.ts: KEEP_ALIVE_PATHS now derives from TAB_ORDER (single
  source of truth) plus /dashboard/discover, deliberately withholding
  /dashboard/settings even though it's in TAB_ORDER — Settings.vue's child
  sections (SystemDangerZone's reboot poll interval,
  VpnStatusSection/KioskDisplaySection/TransportPrefsCard/ClaudeAuthSection's
  one-shot onMounted fetches) were never in this plan's file scope and would
  misbehave under KeepAlive exactly as this plan exists to prevent. Every
  other TAB_ORDER path measured Remounted:true or was unmeasured in
  02-FINDINGS.md, so per the plan's literal exclusion rule (only a measured
  Remounted:false excludes) they all stay registered, including Mesh and
  Chat.
- keepAliveLifecycle.test.ts extended (in the prior commit) with
  shouldKeepAlive true/false assertions across every registered path and six
  secondary-screen paths, plus the KEEP_ALIVE_MAX+2 eviction test against the
  real DashboardRouterView + KEEP_ALIVE_PATHS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 15:06:03 -04:00
archipelago
385c9d866e feat(02-02): tracer tab survives KeepAlive round-trip with revalidation
All checks were successful
Demo images / Build & push demo images (push) Successful in 3m48s
Wires the phase's shared architecture end to end through one main tab
(Marketplace, the worst-measured revisit per 02-FINDINGS.md):

- keepAliveRoutes.ts: exact-match route classifier (shouldKeepAlive,
  KEEP_ALIVE_PATHS, KEEP_ALIVE_MAX=6), seeded with only the tracer tab's
  path. Deliberately not KeepAlive `include` name-matching (async
  components under `<script setup>` have no inferable name).
- DashboardRouterView.vue: extracts Dashboard.vue's nested RouterView into
  a host where <KeepAlive> is a permanent element (never torn down by
  v-if) with its child conditionally present via shouldKeepAlive(route);
  a sibling Transition renders non-cached routes. Both original wrapper
  shapes (full-bleed chat/mesh vs. padded/scrollable default) are
  preserved via computed helpers on one stable, unkeyed wrapper div; the
  :key moves onto <component> itself. Adds per-route scroll retention
  since the scroll container is now stable across navigations.
- useCachedResource.ts: registers onActivated(() => refreshIfStale())
  alongside the existing onScopeDispose block, closing the gap where a
  kept-alive tab would otherwise never revalidate on reactivation
  (onScopeDispose doesn't fire on deactivate; window focus doesn't fire
  on an in-SPA tab switch). No-ops safely for all 8 existing consumers
  outside a KeepAlive boundary.
- useRouteTransitions.ts: exports TAB_ORDER so 02-04 can widen
  KEEP_ALIVE_PATHS from the same source of truth.
- Dashboard.vue: renders DashboardRouterView in place of the inline
  block; removes the now-superseded detail-route scroll save/restore
  (querySelector target no longer exists post-restructure — the new
  per-route Map in DashboardRouterView.vue is a strict superset).

Tests: keepAliveTabs.test.ts proves an included path's instance survives
a round trip (1 mount, 2 activations) while a detail path remounts (2
mounts); useCachedResource.test.ts proves no refetch inside the TTL,
exactly one refetch after it lapses, safe use outside KeepAlive, and
keep-last-value + sticky-ready semantics on a rejected refresh.

Full suite (706 tests), type-check, and build all green; built bundle
carries the new KeepAlive wiring (web/dist/neode-ui/assets).
2026-07-30 07:15:17 -04:00