From 284f6fce39594c155a251c392e074bb2f83bd513 Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 30 Jul 2026 03:50:18 -0400 Subject: [PATCH] docs(02): capture phase context --- .../phases/02-ui-performance/02-CONTEXT.md | 170 ++++++++++++++++++ .../02-ui-performance/02-DISCUSSION-LOG.md | 170 ++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 .planning/phases/02-ui-performance/02-CONTEXT.md create mode 100644 .planning/phases/02-ui-performance/02-DISCUSSION-LOG.md diff --git a/.planning/phases/02-ui-performance/02-CONTEXT.md b/.planning/phases/02-ui-performance/02-CONTEXT.md new file mode 100644 index 00000000..b0b81a9a --- /dev/null +++ b/.planning/phases/02-ui-performance/02-CONTEXT.md @@ -0,0 +1,170 @@ +# Phase 2: UI Performance - Context + +**Gathered:** 2026-07-30 +**Status:** Ready for planning + + +## Phase Boundary + +Make the existing neode-ui Vue SPA feel fast: main-tab switches render immediately from +cached state with background refresh (PERF-02), secondary screens open without blocking +reloads and are instant on repeat visits (PERF-03), and every fix is preceded by profiling +that names the measured cause (PERF-01). Verified on real node hardware (archi-dev-box), +not just the dev workstation. Plus two folded-in AIUI UX defaults (see D-14) — no other +AIUI/content-federation work belongs here. + + + + +## Implementation Decisions + +### Caching Approach (PERF-02) +- **D-01:** Main tabs use BOTH `` around the RouterView (component instances, + scroll position, and in-page state survive tab switches) AND `useCachedResource` + stale-while-revalidate for their data. Today no `` exists anywhere — every + tab switch fully remounts the view. +- **D-02:** Which views get converted is decided by profiling (D-09), not blanket + conversion — "fixes are targeted, not guessed" (PERF-01). Already-fast views are left + alone. +- **D-03:** Heavy views (Mesh with its D3 graph + Leaflet map) ARE kept alive, but + KeepAlive's `max` instance cap is set so the oldest unused views evict — bounded memory + on low-power nodes. +- **D-04:** Secondary screens (AppDetails, server sub-pages, etc.) get `useCachedResource` + keyed per item (e.g. `app-details:${appId}`) but NO KeepAlive — unbounded item counts + would bloat an instance cache. Repeat opens paint instantly from the data cache. + +### Refresh / Staleness UX +- **D-05:** Background refresh on a cached view shows a SUBTLE indicator (small + spinner/shimmer in the header/tab area driven by `loadState === 'refreshing'`) — not + invisible, not stale-age badges. +- **D-06:** Default TTL 30s (the hook's default); per-view tuning is Claude's discretion + (shorter for fast-moving data like mesh peers/sync status, longer for near-static data + like the app catalog). +- **D-07:** Background refresh failures are silent keep-last-value — cached data stays on + screen, retry on next focus/TTL; no toast. Errors surface only on explicit user refresh. +- **D-08:** sessionStorage persistence ON (instant paint after reload) except for large + payloads (file lists, media), which stay memory-only — the hook's `persist: false`. + +### Profiling & Verification (PERF-01, PERF-03) +- **D-09:** Profiling starting set = ALL main surfaces: Apps/app store (+ AppDetails), + Mesh, Wallet (+ send flows), Cloud/Files, Server, Network, and Web5. User confirmed all + of these feel slow, "often app store". +- **D-10:** Profiling produces a COMMITTED findings doc in the phase directory: each slow + surface → measured cause (remount storm / serial RPC waterfall / uncached fetch) → + intended fix. Written and committed BEFORE fixes land. +- **D-11:** On-device verification target is **archi-dev-box**. Pass bar: NO visible + spinner/blank on revisit of a tab or secondary screen already visited this session; + first visits may still show loading. + +### Fix Scope +- **D-12:** Backend (`core/`) changes are ADDITIVE ONLY: new aggregate/batch RPC endpoints + and cheap response-shaping are allowed when profiling names a backend cause; NO refactors + of existing handlers and NOTHING touching the orchestrator (keeps the lifecycle gate out + of play). +- **D-13:** Serial fetch waterfalls in views are fixed client-side by parallelizing + (`Promise.all`) plus rpc-client request dedup. Aggregate endpoints (per D-12) only where + a screen genuinely needs 3+ dependent calls. +- **D-14:** Folded-in AIUI UX defaults (small, in-scope ride-alongs): (a) the AIUI chat + starts EXPANDED, (b) on mobile AIUI starts on the CHAT view, not the context view + (current start-on-context is wrong). Everything else AIUI-related is deferred (see + Deferred Ideas). +- **D-15:** Deploy discipline: dev pair only this phase — no OTA. Fleet rollout rides the + normal release train later. + +### Claude's Discretion +- Per-view TTL values (D-06). +- KeepAlive `max` cap value and eviction tuning (D-03). +- Choice between client-side parallelization and a new aggregate endpoint per screen, + within D-12/D-13 bounds. +- Exact placement/styling of the subtle refresh indicator (D-05), consistent with the + existing design system. + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Frontend caching / data layer +- `neode-ui/src/composables/useCachedResource.ts` — The existing SWR hook (memory → + sessionStorage → fetch, sticky-ready, refreshing state, focus revalidation, + abort-on-unmount). The pattern to EXTEND, not reinvent. +- `neode-ui/src/stores/resources.ts` — Shared resource store backing the hook. +- `neode-ui/src/api/rpc-client.ts` — RPC client (retry/backoff, dedup option, abort + signals) — waterfall fixes build on its dedup + signal support. +- `neode-ui/src/App.vue` — Root RouterView (line ~8) where KeepAlive wrapping lands; + note the splash/isReady gating around first reveal. + +### Project-level +- `.planning/codebase/CONVENTIONS.md` — Vue/TS conventions (script setup, composable + patterns, store patterns). +- `.planning/codebase/STRUCTURE.md` — Where views/components/stores/composables live. +- `CLAUDE.md` — Commit/push discipline, build notes (grep built bundle before shipping), + invariants. + + + + +## Existing Code Insights + +### Reusable Assets +- `useCachedResource` + `resources` store: production-quality SWR already used by 8 views + (Monitoring, Cloud, Server, Federation, Credentials, Web5, FIPS cards) — main tabs just + don't use it yet. +- `rpc-client.ts` `dedup: true` option: shares in-flight requests across concurrent + callers — key for waterfall/parallelization fixes. + +### Established Patterns +- 44 routes are already lazy-loaded (`import()` in `router/index.ts`) — route-level code + splitting is NOT the problem. +- Views fire multiple `onMounted` fetches (Cloud 4, Server/Mesh/ContainerAppDetails 3…) — + the likely refetch-on-remount storm PERF-01 will quantify. +- No `` anywhere in the codebase today. + +### Integration Points +- `App.vue` RouterView is the single mount point for KeepAlive. +- Pinia stores (`stores/*.ts`) hold global state; per-view fetch logic migrates into + `useCachedResource` keys rather than new ad-hoc stores. + + + + +## Specific Ideas + +- UI changes verified on the :8100 dev preview against archi-dev BEFORE deploy (carried + forward from Phase 1 discipline). +- Frontend build gotcha (CLAUDE.md): grep `web/dist/neode-ui` for new strings after + `npm run build` — the build can silently no-op. +- Pass bar phrasing from the user: revisited surfaces paint content immediately; the + sluggishness reported on-device must be gone on archi-dev-box. + + + + +## Deferred Ideas + +Captured verbatim from the user during this discussion — new capabilities that belong in +their own phase (candidate: "AIUI & Content Federation"; add via `/gsd-phase add`): + +- **AIUI permissioned node access:** the AI UI talks to the node safely when permissioned, + without leaking data, using the same command surface as Pine and everything else + enableable in settings. +- **AIUI content deep-dive:** deep dive into the content AIUI finds and serves — "make it + perfect". +- **AIUI peer media:** show peer videos and files in AIUI. +- **IndeeHub cross-node content source with payments:** peer files/videos made available + on IndeeHub by other nodes for films — an "archipelago content source" so any IndeeHub + install plugs into every node's uploaded/served IndeeHub content, with payments; same + for music files and everything else. +- **AIUI Nostr integration polish:** make the Nostr integration in AIUI more beautiful. + +(The two small AIUI UX defaults — chat starts expanded; mobile starts on chat — were +folded INTO this phase as D-14 and are not deferred.) + + + +--- + +*Phase: 2-UI Performance* +*Context gathered: 2026-07-30* diff --git a/.planning/phases/02-ui-performance/02-DISCUSSION-LOG.md b/.planning/phases/02-ui-performance/02-DISCUSSION-LOG.md new file mode 100644 index 00000000..4d18ead4 --- /dev/null +++ b/.planning/phases/02-ui-performance/02-DISCUSSION-LOG.md @@ -0,0 +1,170 @@ +# Phase 2: UI Performance - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-07-30 +**Phase:** 2-ui-performance +**Areas discussed:** Caching approach, Refresh/staleness UX, Profiling targets, Fix scope: backend RPC + +--- + +## Caching approach + +| Option | Description | Selected | +|--------|-------------|----------| +| KeepAlive + SWR | KeepAlive for main tabs AND useCachedResource for data | ✓ | +| SWR only | Extend useCachedResource, keep full remounts | | +| KeepAlive only | Instances alive, ad-hoc data refresh | | + +**User's choice:** KeepAlive + SWR (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| Profiling decides | Convert only surfaces PERF-01 names as slow | ✓ | +| All main tabs | Blanket conversion | | + +**User's choice:** Profiling decides (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| Keep alive, cap count | Include heavy views (Mesh D3+Leaflet), KeepAlive max cap | ✓ | +| Exclude heavy views | Only lightweight tabs kept alive | | +| You decide | Per-view by measurement | | + +**User's choice:** Keep alive, cap count (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| SWR, no KeepAlive | Per-item cache keys, no instance cache | ✓ | +| Same as main tabs | KeepAlive + SWR for secondary screens too | | +| You decide | Per screen by cardinality | | + +**User's choice:** SWR, no KeepAlive for secondary screens (recommended) + +--- + +## Refresh/staleness UX + +| Option | Description | Selected | +|--------|-------------|----------| +| Invisible | Content updates in place, no indicator | | +| Subtle indicator | Small spinner/shimmer while refreshing | ✓ | +| Stale-age badges | Show data age via isStale/ageMs | | + +**User's choice:** Subtle indicator (declined the "Invisible" recommendation) + +| Option | Description | Selected | +|--------|-------------|----------| +| 30s, per-view tuning | Hook default; Claude tunes per view | ✓ | +| Short (10s) everywhere | Fresher, more RPC chatter | | +| Long (60s+) everywhere | Minimal chatter | | + +**User's choice:** 30s with per-view tuning (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| Silent keep-last | Keep cached data, retry on focus/TTL | ✓ | +| Toast on failure | Surface every background failure | | + +**User's choice:** Silent keep-last (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| Yes, except large payloads | sessionStorage persist, skip big lists | ✓ | +| Memory only | Cold start on every reload | | + +**User's choice:** Persist except large payloads (recommended) + +--- + +## Profiling targets + +| Option | Description | Selected | +|--------|-------------|----------| +| Apps tab + AppDetails | App browser + detail/log screens | ✓ | +| Mesh tab | Map, contacts, messages | ✓ | +| Wallet tab + send flows | Balances, send/receive | ✓ | +| Cloud/Files + Server | File browser, node status/settings | ✓ | + +**User's choice:** All four, plus free-text additions: "network and web 5 and cloud and often app store" + +| Option | Description | Selected | +|--------|-------------|----------| +| .228 | Designated gate/test node | | +| .198 dev pair | Other dev-pair node | | +| Whichever node showed it | User names the node | | + +**User's choice:** Free-text: **archi-dev-box** + +| Option | Description | Selected | +|--------|-------------|----------| +| Committed findings doc | Profiling report in phase dir before fixes | ✓ | +| Findings in plans only | No standalone report | | + +**User's choice:** Committed findings doc (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| No visible spinner on revisit | Subjective-but-crisp pass bar | ✓ | +| Numeric budgets | ms budgets measured on-device | | + +**User's choice:** No visible spinner on revisit (recommended) + +--- + +## Fix scope: backend RPC + +| Option | Description | Selected | +|--------|-------------|----------| +| Additive changes only | New aggregate/batch endpoints ok; no refactors/orchestrator | ✓ | +| Frontend only | Backend untouched | | +| Whatever profiling justifies | Any backend change, gate re-runs as needed | | + +**User's choice:** Additive changes only (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| Parallelize + dedup | Promise.all + rpc-client dedup | ✓ | +| Aggregate endpoints | Combined-payload RPCs for 3+ call screens | | +| You decide per view | Per-screen by measurement | | + +**User's choice:** Parallelize + dedup (recommended) + +| Option | Description | Selected | +|--------|-------------|----------| +| Dev pair only, no OTA | Verify on dev nodes; fleet rollout via release train | ✓ | +| OTA when verified | Fleet OTA as part of this phase | | + +**User's choice:** Dev pair only (recommended) + +--- + +## Scope routing (freeform input) + +During area selection the user requested a large AIUI/IndeeHub scope expansion. Routed via +a follow-up question: + +| Option | Description | Selected | +|--------|-------------|----------| +| New phase, discuss next | Add roadmap phase + full discuss-phase | | +| Backlog only for now | Keep in Deferred Ideas | | +| Fold the small UX fixes into Phase 2 | Chat-expanded + mobile-starts-on-chat ride along; big work deferred | ✓ | + +**Notes:** The two UX defaults became D-14 in CONTEXT.md. All other AIUI/IndeeHub items +recorded verbatim in Deferred Ideas. + +## Claude's Discretion + +- Per-view TTL values +- KeepAlive max cap / eviction tuning +- Parallelization vs aggregate endpoint per screen (within additive-only bound) +- Refresh indicator placement/styling + +## Deferred Ideas + +- AIUI permissioned node access (Pine-parity command surface, no data leaks) +- AIUI content deep-dive ("make it perfect") +- AIUI peer videos and files +- IndeeHub cross-node content source with payments (films, music, everything) +- AIUI Nostr integration polish