Files
archy/.planning/phases/02-ui-performance/02-01-SUMMARY.md
T

19 KiB
Raw Blame History

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status
02-ui-performance 01 ui
playwright
vue3
performance-profiling
keepalive
rpc-tracing
e2e
Re-runnable Playwright perf harness (neode-ui/e2e/perf/{surfaces,measure,surface-perf.spec}.ts)
Committed on-device baseline (02-PERF-BASELINE.json) taken against archi-dev-box
D-10 findings doc (02-FINDINGS.md) mapping every measured D-09 surface to a cited cause, fix, and owning plan
02-02-app-store-tracer
02-03-secondary-screens
02-04-keepalive-lifecycle
02-05-mesh
02-06-server-home
02-07-chat-aiui
02-08-verify
added patterns
Playwright perf harness: real UI clicks (RouterLink/button) for all navigation, never page.goto between surfaces, so revisit timing reflects genuine Vue Router client-side transitions
Dataset-stamp remount probe: stamp rootSelector's DOM node post-first-visit, read it back post-revisit to detect component-instance reuse vs. destroy/recreate
Sweep-line RPC concurrency derivation (maxConcurrentRpc/rpcWallClockMs) to distinguish serial waterfalls from parallel fan-outs from raw call timing, without re-reading source
Dismiss-and-retry click guard for stray full-screen overlays (app-engagement modals) that can appear mid-navigation on real hardware
created modified
neode-ui/e2e/perf/surfaces.ts
neode-ui/e2e/perf/measure.ts
neode-ui/e2e/perf/surface-perf.spec.ts
.planning/phases/02-ui-performance/02-PERF-BASELINE.json
.planning/phases/02-ui-performance/02-FINDINGS.md
Marketplace is the tracer tab for 02-02 — worst-measured main tab (2033ms revisit) and matches the user's own top complaint ('often app store')
No surface in the measured D-09 set shows a serial-RPC-waterfall signature; ContainerAppDetails.vue (RESEARCH.md's one confirmed waterfall target) is confirmed fully unreachable dead code this session (zero grep matches, no importer, no route entry) — D-13's parallelization pattern has no live target in this phase
3 rows (Marketplace, MarketplaceAppDetails, OpenWrtGateway) have RPC evidence confounded by the harness's navSteps transiting another main tab first, which fires that tab's own onMounted burst; classified conservatively as remount storm rather than uncached fetch, with the confound spelled out for downstream plans to re-verify
Home, Apps, Cloud, Fleet measured already fast on real hardware — left alone per D-02, no useCachedResource conversion planned, only shared KeepAlive wrapping
Mesh and Chat recorded as unmeasured with specific reasons (device-not-connected timeout; AIUI connecting-overlay blocking the close click), never presented as already-fast
Perf harness navigation contract: every SURFACES row navigates via real UI clicks (navSteps) with an explicit away step (neutral Settings tab, or a closeSelector for in-page triggers like modals) — page.goto is reserved for the initial goHome() recovery fallback only, never for measured transitions
PERF-01
id description requirement verification human_judgment
D1 Re-runnable surface perf harness (SURFACES table, measureSurface(), Playwright spec) covering every D-09 surface plus 4 secondary screens and the located wallet-send modal PERF-01
kind ref status
e2e neode-ui/e2e/perf/surface-perf.spec.ts — verified end-to-end against both the local mock backend and archi-dev-box (real hardware), exit code 0 each time pass
false
id description requirement verification human_judgment
D2 On-device baseline (02-PERF-BASELINE.json) recorded against archi-dev-box with 13/15 surfaces measuring cleanly, 2 recorded unmeasured with reasons PERF-01
kind ref status
automated_ui Task 2 acceptance script — rows=15, every row has either numeric measurements or a non-empty error string, no neode-ui/src changes, redaction check clean pass
false
id description requirement verification human_judgment
D3 D-10 findings doc committed, mapping every surface to a measured Primary Cause (from the closed set), Intended Fix, and Owning Plan, before any neode-ui/src change lands in the phase PERF-01
kind ref status
automated_ui Task 3 acceptance script — all 6 required headings present, ContainerAppDetails grep output pasted verbatim, git diff --name-only HEAD~2..HEAD -- neode-ui/src empty pass
false
~100min (includes one human-action checkpoint pause for the archi-dev-box credential) 2026-07-30 complete

Phase 02 Plan 01: Surface Perf Harness & D-10 Findings Summary

Built a re-runnable Playwright perf harness, measured 13/15 D-09 surfaces on real archi-dev-box hardware, and committed the D-10 findings doc that names Marketplace (2033ms revisit) as the worst offender and the 02-02 tracer pick — while also confirming ContainerAppDetails.vue (RESEARCH.md's one "confirmed" serial-waterfall target) is fully unreachable dead code, leaving no live waterfall target in this phase.

Performance

  • Duration: ~100 min total (includes a human-action checkpoint pause: the harness build/verify was quick, but Task 2 was blocked on the archi-dev-box UI password until the coordinator provided it)
  • Completed: 2026-07-30
  • Tasks: 3/3 completed
  • Files modified: 5 created (3 harness files, 1 baseline artifact, 1 findings doc), 0 files modified under neode-ui/src

Accomplishments

  • neode-ui/e2e/perf/{surfaces,measure,surface-perf.spec}.ts — a re-runnable Playwright harness covering all 10 D-09 primary surfaces (Home, Apps, Marketplace, Discover, Cloud, Mesh, Server, Web5, Fleet, Chat), 4 secondary screens (AppDetails, MarketplaceAppDetails, CloudFolder, OpenWrtGateway), and the located wallet-send flow (a modal on the Home wallet card — RESEARCH.md found no Wallet.vue). Navigation is via real UI clicks (never page.goto between surfaces) so revisit timing reflects genuine Vue Router client-side transitions, not full page reloads.
  • .planning/phases/02-ui-performance/02-PERF-BASELINE.json — committed baseline taken against archi-dev-box (real node hardware, the D-11 verification target), 3 runs per surface. 13/15 surfaces measured cleanly; Mesh and Chat recorded unmeasured with specific, cited reasons.
  • .planning/phases/02-ui-performance/02-FINDINGS.md — the D-10 gate doc. Every row's Primary Cause cites a 02-PERF-BASELINE.json field. Key findings: 4 main tabs are already fast and left alone; Marketplace is the tracer pick for 02-02; no surface shows a serial-waterfall signature; a measurement confound (RPC bleed-through from an intermediate main-tab hop in 3 rows' nav chains) is documented rather than masked; Wallet-send's revisit being slower than its first visit is flagged as a genuine anomaly for 02-03 to profile directly.

Task Commits

Each task was committed atomically:

  1. Task 1: Build the re-runnable surface perf harness - a75b6709 (feat)
  2. Task 2: Record the on-device baseline from archi-dev-box - 36145140 (fix — includes the baseline artifact plus harness hardening discovered while running it against real hardware)
  3. Task 3: Write and commit the D-10 findings doc - 675deb65 (docs)

No separate plan-metadata commit was made for this SUMMARY — this file, STATE.md, and ROADMAP.md are committed together in the final commit below.

Files Created/Modified

  • neode-ui/e2e/perf/surfaces.ts - SURFACES table: one row per D-09 surface + secondary screens + wallet-send, with real-click navSteps, contentSelector, rootSelector
  • neode-ui/e2e/perf/measure.ts - measureSurface(): first-visit/revisit timing, RPC trace, remount probe, sweep-line concurrency derivation, dismiss-and-retry overlay guard
  • neode-ui/e2e/perf/surface-perf.spec.ts - Playwright spec: logs in via the existing app-launch.spec.ts flow, walks every surface, writes the JSON artifact
  • .planning/phases/02-ui-performance/02-PERF-BASELINE.json - Committed on-device baseline (archi-dev-box, 3 runs/surface)
  • .planning/phases/02-ui-performance/02-FINDINGS.md - D-10 findings doc: per-surface cause table, ranked fix order, surfaces left alone, corrections to prior research, owning-plan map

Decisions Made

  • Marketplace is the 02-02 tracer tab — worst-measured main tab (2033ms revisit) and matches the user's specific complaint ("often app store").
  • No serial-RPC-waterfall Primary Cause was assigned anywhere — every surface with 2+ RPC calls on revisit showed maxConcurrentRpc at or near its total call count (already parallel). ContainerAppDetails.vue, RESEARCH.md's one confirmed waterfall target, is confirmed unreachable dead code this session (grep -rn "ContainerAppDetails" neode-ui/src returns nothing — no importer, no route entry).
  • 3 rows' RPC evidence is flagged as confounded, not silently trusted: Marketplace, MarketplaceAppDetails, and OpenWrtGateway all reach their target via a navSteps chain that transits another main tab first (Home or Server), whose own onMounted RPC burst is still resolving when the harness's tracker starts recording — captured method names for OpenWrtGateway are an exact subset of Server.vue's own known call set, confirming the confound rather than assuming it. These rows are classified conservatively (remount storm) with the confound spelled out for 02-02/02-03 to re-verify with DevTools.
  • Home, Apps, Cloud, Fleet are left alone (D-02) — all measured sub-500ms revisit with 0-1 trivial RPC calls; no useCachedResource conversion planned for their data layer, only the shared <KeepAlive> wrapping from 02-04.
  • Wallet-send's anomaly (revisit consistently slower than first-visit, 2607ms vs 735ms median, across all 3 runs) is flagged explicitly rather than smoothed over, since it has zero RPC either time and needs direct DevTools profiling in 02-03 to explain.
  • Auth gate handled via checkpoint, not guessed: archi-dev-box's real UI password was unknown and not discoverable from this environment (checked scripts/deploy-config.sh — gitignored, not present on this machine; no plaintext credential found via SSH). Rather than falling back to the mock-backend baseline silently or guessing a password, the plan paused at a checkpoint:human-action and resumed once the coordinator supplied the credential. The password itself was never written to any committed file — passed only via the ARCHY_PASSWORD environment variable at runtime.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Cascading harness failures from an unhandled Companion-app auto-show overlay

  • Found during: Task 1 verification (local mock-backend run)
  • Issue: A once-per-browser Companion-app intro overlay (CompanionIntroOverlay.vue, opened via useCompanionIntro's auto-show gate) can appear mid-click on the Home page, intercepting every subsequent sidebar click for the rest of the run — one surface's stray UI cascaded failures into every remaining surface.
  • Fix: Added dismissOverlays() (Escape key + any [aria-label*="Close" i] button inside a dialog/full-screen overlay) called before every navigation click, wrapped in a clickWithGuard() retry loop (3 attempts, dismissing between each) since the overlay can appear after the initial dismiss check but before Playwright's own actionability wait resolves.
  • Files modified: neode-ui/e2e/perf/measure.ts
  • Verification: Re-ran the harness against the mock backend; surfaces after the overlay's first appearance (previously 100% failure) measured cleanly.
  • Committed in: a75b6709 (Task 1 commit)

2. [Rule 1 - Bug] Fleet's "Fleet" link selector matched a CSS-hidden duplicate

  • Found during: Task 1 verification
  • Issue: Web5Federation.vue renders two "Fleet" RouterLinks — one inside .web5-card-actions-top (permanently display: none per a project-wide "compact header variants are retired" CSS rule) and the real, visible one inside .web5-card-actions-bottom-grid. a:has-text("Fleet") with .first() matched the hidden copy.
  • Fix: Scoped the selector to .web5-card-actions-bottom-grid a:has-text("Fleet").
  • Files modified: neode-ui/e2e/perf/surfaces.ts
  • Verification: Fleet measured cleanly in the next mock-backend run.
  • Committed in: a75b6709 (Task 1 commit)

3. [Rule 1 - Bug] Chat's hidden sidebar broke every subsequent surface's recovery navigation

  • Found during: Task 1 verification, then again (worse) against real archi-dev-box hardware in Task 2
  • Issue: DashboardSidebar.vue is v-show="!chatFullscreen" — once on /dashboard/chat, the sidebar (and the neutral Settings link every other surface's "away" step and every surface's goHome() depend on) is invisible. On real hardware, AIUI's own "connecting" overlay could also outlive the harness's close-button click budget, leaving the run permanently stuck on Chat with a hidden sidebar and no way back — every surface measured after Chat failed.
  • Fix: Gave Chat its own closeSelector (.chat-close-btn, which calls closeChat()'s router.back()) instead of the generic Settings-tab away-step. Added a hard page.goto('/dashboard') recovery fallback inside goHome() for the case where even the close button is unreachable — an explicit, narrowly-scoped exception to the "no page.goto between surfaces" rule, used only to break out of a stuck state, never to measure one.
  • Files modified: neode-ui/e2e/perf/surfaces.ts, neode-ui/e2e/perf/measure.ts
  • Verification: Re-ran against archi-dev-box; all surfaces after Chat (previously cascading to 100% failure) measured cleanly, while Chat itself remains honestly recorded as unmeasured (AIUI's real connection latency exceeds the harness's budget on this hardware — a genuine finding, not a harness bug).
  • Committed in: 36145140 (Task 2 commit)

4. [Rule 1 - Bug] dismissOverlays() prematurely closed the exact dialog a closeSelector step meant to close

  • Found during: Task 1 verification
  • Issue: dismissOverlays() treats any [aria-label*="Close" i] button inside a dialog as a stray overlay to dismiss. When the away-step's own deliberate close click (surface.closeSelector) was routed through the same clickWithGuard() helper, dismissOverlays() closed the modal a beat before the explicit click ran, leaving that click with nothing to find (0 matches, immediate failure) for both Wallet-send and Chat.
  • Fix: The closeSelector away-step click is a plain page.locator(...).click(), not routed through clickWithGuard/dismissOverlays.
  • Files modified: neode-ui/e2e/perf/measure.ts
  • Verification: Wallet-send and Chat's away-steps stopped failing on a 0-match locator.
  • Committed in: a75b6709 (Task 1 commit)

5. [Rule 1 - Bug] Run header's commit field always recorded 'unknown'

  • Found during: Task 2, comparing the baseline artifact's header against Task 2's own acceptance criteria (baseUrl, takenAt, commit, runs, notes must all be recorded)
  • Issue: currentCommit() shelled out to git rev-parse --short HEAD using { cwd: __dirname }, but __dirname is unavailable under neode-ui's "type": "module" ESM runtime — the resulting ReferenceError was silently swallowed by the existing catch block, always yielding 'unknown'.
  • Fix: Use process.cwd() instead (Playwright always sets this to the project root it was invoked from).
  • Files modified: neode-ui/e2e/perf/surface-perf.spec.ts
  • Verification: Re-ran against archi-dev-box; the artifact's header now records the real short commit hash (a75b6709).
  • Committed in: 36145140 (Task 2 commit)

Total deviations: 5 auto-fixed (all Rule 1 — bugs found and fixed during the harness's own verification passes, not scope creep against the plan's task boundaries). Impact on plan: All five were necessary for the harness to actually produce a trustworthy, non-cascading, correctly-labeled baseline. None touched neode-ui/src.

Issues Encountered

  • Authentication gate on Task 2: archi-dev-box's real UI password was unknown and not derivable from this environment (the standard dev/test password used elsewhere in the repo, password123, was rejected). Checked for a scripted credential (scripts/deploy-config.sh, gitignored, documented by scripts/deploy-config.example) — the actual file doesn't exist on this machine (likely lives on the ThinkPad build server per project memory). Stopped and returned a checkpoint:human-action rather than falling back to a mock-backend baseline silently or attempting to reset the node's stored credential (out of scope, and risky on a shared dev node). The coordinator supplied the real password; execution resumed immediately and completed Tasks 23 without further blockers.
  • Measurement confound discovered and documented, not hidden: while writing the findings doc, comparing captured RPC method names across surfaces revealed that 3 rows' navigation chains transit another main tab before reaching their target, causing that intermediate tab's own onMounted RPC burst to be misattributed to the destination surface. Rather than presenting the raw (confounded) RPC counts as clean "uncached fetch" evidence — which the plan's own prohibitions explicitly forbid ("MUST NOT present inferred, code-read, or cherry-picked numbers as measured profiling results") — those 3 rows were reclassified conservatively and the confound spelled out for downstream plans.

User Setup Required

None — no external service configuration required. The archi-dev-box password used for this run was supplied out-of-band by the coordinator and passed only via the ARCHY_PASSWORD environment variable at runtime; it is not stored in any file in this repository.

Next Phase Readiness

  • .planning/phases/02-ui-performance/02-FINDINGS.md is committed and ready for 02-02 through 02-08 to consume — each has a clear owning-plan assignment, a cited Primary Cause, and (where applicable) an explicit measurement caveat to re-verify before implementing.
  • The harness (neode-ui/e2e/perf/) is re-runnable as-is for 02-08's after-artifact comparison pass — no changes needed for that later plan to reuse it against the same archi-dev-box target.
  • Two open items carry forward: Mesh needs its own on-node profiling pass during 02-05 (this session's device-not-connected timeout may not reflect steady-state behavior); Chat's AIUI connection latency on real hardware (long enough to block even the close-button click) is itself a data point worth 02-07 investigating directly, separate from the two small D-14 UX defaults already scoped there.

Phase: 02-ui-performance Completed: 2026-07-30