23 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | requirements-completed | coverage | duration | completed | status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-ui-performance | 11 | ui |
|
|
|
|
|
|
|
~230min | 2026-07-31 | complete |
Phase 02 Plan 11: Profile, Fix, and Prove the Six Confirmed Timing Regressions Summary
A real CPU/rendering profile (not a guess) showed the six regressed surfaces' cost was 86-99% idle/program time, not compute — redirecting the investigation to three background pollers (Fleet/Server/Web5 sub-components) that armed a setInterval in onMounted and never disarmed it once KeepAlive kept their owning views alive forever; all three fixed and proven on archi-dev-box (web5 275ms, server 574ms, fleet 790ms — down from 1329ms/1239ms/2631ms regressed). Discover's separate CSS entrance-animation-replay cause was found and evidenced but not fixed (blast radius exceeds this plan's scope); openwrt-gateway's final run crashed (no data) and its prior numbers are flagged, not retracted.
Performance
- Duration: ~230 min (Task 1 profiling ~40min including waiting for a concurrent cargo build on the shared box to clear before taking timed measurements; Task 2 fix + TDD ~60min; Task 3 deploy + re-measure + coordinator-directed follow-up investigation ~130min)
- Completed: 2026-07-31
- Tasks: 3 (all
type="auto", no checkpoints — autonomous plan) - Files modified: 8 (2 created:
profile-revisit.spec.ts,02-PERF-FINAL.json; 6 modified:02-FINDINGS.md,REQUIREMENTS.md,useFleetData.ts,FipsNetworkCard.vue,Web5Monitoring.vue,keepAliveLifecycle.test.ts) plusSTATE.md/ROADMAP.mdstate-tracking updates
Accomplishments
- Built
neode-ui/e2e/perf/profile-revisit.spec.ts— an additive CDP-based diagnostic (Profiler CPU sampling + Tracing devtools.timeline events + setTimeout/rAF instrumentation + CSS transition/animation event log + an independent first-paint probe), never touching the frozen 02-01 harness files. - Named the real cause before any fix (D-10): the CPU profile showed
(idle)+(program)at 86-99% of every revisit window across all six surfaces, with combined app-JS self-time under 10% everywhere — directly ruling out the objective's own listed hypotheses (expensive computed re-evaluation, watcher cascades, whole-subtree re-renders, per-row recompute) as the dominant cost. - Found three leaked background pollers via a source-level lifecycle-audit follow-up (extending 02-04's own grep past the top-level view files it checked, into the child components/composables those views delegate to):
useFleetData.ts(Fleet's own composable, 60s poll),FipsNetworkCard.vue(rendered inside Server.vue, 15s poll),Web5Monitoring.vue(rendered inside Web5.vue, 30s poll — redundant with Home.vue's own correctly-gated 10s poll of the same store). All three armed their interval inonMountedand only disarmed it inonUnmounted/onBeforeUnmount— harmless before 02-04's KeepAlive registration (the view was destroyed on every tab-away, so the teardown hook fired every time) and a permanent, session-long background-RPC leak once KeepAlive kept the instance alive forever. - Fixed all three with the exact
onActivated/onDeactivatedarm/disarm pattern 02-04 already established and proved safe for Server.vue's ownvpnPollInterval— no new pattern invented. Added three regression tests tokeepAliveLifecycle.test.ts, confirmed RED against the pre-fix code (git stash) before confirming GREEN with the fix restored. - Deployed frontend-only to archi-dev-box and re-ran the frozen harness (5 runs/surface,
02-PERF-FINAL.json). Built a four-way (Baseline → After → Remeasure → Final) dispersion comparison. - Investigated two coordinator-raised follow-ups directly, not assumed:
- OpenWrtGateway: this run's 5/5 samples failed with a Chromium
Target crashederror cascading from an unrelated surface (cloud-folder) earlier in the same harness run — recorded as not-measurable, never written in as data. Separately confirmed via source that the harness's owncontentSelector(h1, unconditional markup) and the frontend's deterministic "No router configured" → Connect-form render mean the prior baseline/after/remeasure numbers were measuring a real, substantive disconnected-state UI, not an empty/error page — the six-surface regression count is not retracted on this basis. - Discover (1389ms, worst remaining, least improved): profiled directly and found a genuine SECOND cause —
card-stagger/showStaggerentrance-animation classes are correctly gated as "once per session" at the JS level (a module-scoped const, mirroring Web5's own correct pattern) but are never programmatically REMOVED from the DOM after their one intended play, so every KeepAlive detach/reattach cycle restarts the CSS animation on reactivation. Confirmed via a standalone diagnostic (app-card count transiently doubling — 19→34→19 — on every single revisit) and an extendedanimationstart/animationendevent log spanning the whole window. Not fixed — the safe fix touches 5+ files outside this plan's scope and needs its own real-device verification budget; named and evidenced, recommended as a dedicated follow-up.
- OpenWrtGateway: this run's 5/5 samples failed with a Chromium
Task Commits
- Task 1: Profile each surface's revisit window and name the real cause per surface —
050a87d2(feat) - Task 2: Fix each surface's named cause where fixable —
2c25e512(fix) - Task 3: Deploy, re-measure, four-way comparison, final verdicts —
48a2ff7c(docs)
Plan metadata: (this commit)
Files Created/Modified
neode-ui/e2e/perf/profile-revisit.spec.ts— additive CDP profiling diagnostic (CPU self-time by chunk, Chrome trace event breakdown, timer/rAF capture, CSS transition/animation log, independent first-paint probe); frozen harness confirmed untouched throughout.planning/phases/02-ui-performance/02-FINDINGS.md—## Client-Side Render Cost Root Cause (gap closure, 02-11)(profiling evidence, the three leaked pollers named with file/line, the fix),## Task 3: Deploy, four-way re-measure, and final verdicts(dispersion table, OpenWrtGateway data-integrity note, Discover's second-cause investigation, per-surface verdicts against both bars).planning/REQUIREMENTS.md— PERF-02/PERF-03 rows updated to the final state.planning/STATE.md/.planning/ROADMAP.md— position, progress, decisions, blockers updatedneode-ui/src/views/fleet/useFleetData.ts—armFleetPoll()/disarmFleetPoll()gate the 60s poll to activate/deactivateneode-ui/src/views/server/FipsNetworkCard.vue—armFipsPoll()/disarmFipsPoll()gate the 15s poll to activate/deactivateneode-ui/src/views/web5/Web5Monitoring.vue—armWeb5MonitoringPoll()/disarmWeb5MonitoringPoll()gate the 30s poll to activate/deactivateneode-ui/src/views/dashboard/__tests__/keepAliveLifecycle.test.ts— new02-11 gap closuredescribe block, 3 regression tests
Results — the six named surfaces, both bars reported separately
Per the coordinator's explicit framing: (a) is the phase-2 regression closed (at/below pre-phase-2 baseline), and (b) does it meet Dorian's <300ms target? These differ per surface and neither is used to soften the other.
| Surface | Baseline → Regressed → Final (median revisit ms) | Regression closed? | Meets <300ms? | Verdict |
|---|---|---|---|---|
| web5 | 566 → 1329 → 275 | ✅ Yes (51% below baseline) | ✅ Yes | Fixed. Leaked Web5Monitoring.vue poll. |
| server | 738 → 1239 → 574 | ✅ Yes (22% below baseline) | ❌ No | Regression fixed, stretch target not yet met — residual is real, un-eliminated per-resource reactivation cost (7 useCachedResource groups), not a new defect. |
| fleet | 330 → 2631 → 790 | ⚠️ Substantially improved, not fully back to baseline | ❌ No | Leaked useFleetData.ts poll (worst-magnitude offender) fixed; residual likely ordinary run-to-run dispersion on a shared, loaded box (min sample 298ms, close to baseline). |
| app-details | 1204 → 2668 → 1231 | ✅ Yes (essentially, within 2%) | ❌ No | Restored to baseline; residual is the already-documented useCachedResource per-mount setup cost, a pre-existing trade this phase never promised to remove. |
| discover | 1083 → 1453 → 1389 | ❌ No (28% above baseline) | ❌ No | Second cause found and evidenced (CSS entrance-animation replay on KeepAlive reactivation) but NOT fixed — blast radius exceeds this plan's scope; recommended as a dedicated follow-up. |
| openwrt-gateway | 663.5 → 1460 → no data | N/A | N/A | Not measurable this pass (unrelated browser crash cascaded into this surface); prior numbers stand, confirmed to reflect a real disconnected-device UI render, not an empty page. |
First-paint vs. content-visible (where they diverge meaningfully, from profile-revisit.spec.ts's independent rAF-driven probe, post-fix): Web5 — first-paint 134ms vs. a harness-comparable wall-clock around 275-798ms across runs (the harness's stricter contentSelector-visible field trails first-paint by a wide margin; a human would perceive Web5 as fast well before the harness's own number resolves). Server — first-paint 58ms vs. ~150-574ms. Discover — first-paint 901ms, nearly equal to its own wall-clock (1095ms in the diagnostic run) — for Discover specifically, first-paint does NOT meaningfully diverge from content-visible, because the CSS-animation-replay cause (not fixed this pass) IS the visible cost, not a hidden one behind a stricter check.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Three background pollers left running forever after their owning view was deactivated
- Found during: Task 1 profiling + follow-up source audit
- Issue:
useFleetData.ts,FipsNetworkCard.vue,Web5Monitoring.vueeach armed asetIntervalinonMountedand only cleared it inonUnmounted/onBeforeUnmount— never-firing teardown hooks once their owning views joinedKEEP_ALIVE_PATHSin 02-04. - Fix: Gated arm/disarm to
onActivated/onDeactivated, matching 02-04's own established pattern exactly. - Files modified:
neode-ui/src/views/fleet/useFleetData.ts,neode-ui/src/views/server/FipsNetworkCard.vue,neode-ui/src/views/web5/Web5Monitoring.vue, plus regression tests inkeepAliveLifecycle.test.ts. - Verification: RED confirmed against pre-fix code via
git stash, then GREEN restored. Full suite/type-check/build green. - Committed in:
2c25e512(Task 2 commit).
Scope deviations (documented, not auto-fixed)
1. Discover's second cause (CSS entrance-animation replay) found and evidenced, deliberately not fixed
- Found during: Task 3's coordinator-directed follow-up investigation into Discover specifically.
- Issue:
card-stagger/showStaggerentrance animation classes are baked into the DOM at first mount and never programmatically removed; every KeepAlive reactivation restarts the CSS animation because the browser treats DOM reattachment as a fresh animation start, independent of the correctly-scoped JS "once per session" flag. - Why not fixed: the safe fix (converting
showStaggerto a reactive ref explicitly cleared after the animation completes, forcing an actual class removal) is not confined to Discover.vue — the identical pattern is used byApps.vue/AppCard.vue(Discover's own navSteps transit partner),Marketplace.vue,Home.vue, and four Web5 sub-card files. None of those files are in this plan'sfiles_modified. A change to this class-timing pattern needs the same real-device visual-regression verification 02-02's original KeepAlive rollout required (that pass's own Task 3 checkpoint caught a real regression on its first attempt for exactly this class of change) — disproportionate to add to an already-large gap-closure plan under time pressure, per the coordinator's own explicit instruction not to open-endedly optimize. - Files modified: None (investigation and documentation only).
- Destination: recorded in
02-FINDINGS.md§ Task 3,STATE.mdBlockers/Concerns, andREQUIREMENTS.md's PERF-03 row as a dedicated follow-up item.
2. OpenWrtGateway's final re-measure returned no data; a data-integrity note added to the record instead of silently letting the prior numbers stand unexamined
- Found during: Task 3, after the coordinator flagged that no OpenWrt device has ever been connected to archi-dev-box.
- Issue: This run's 5/5 samples failed with a Chromium
Target crashederror cascading from an unrelated surface (cloud-folder) earlier in the same harness run — a genuine harness-execution failure, not a measurement. - Resolution: Recorded as not-measurable, not written in as data. Separately investigated (not assumed) whether the prior baseline/after/remeasure numbers were measuring a real page or an empty/error state: confirmed via source that they measured a genuine, substantive disconnected-state UI ("Connect to Router" form), so the six-surface regression count stands, with the numbers flagged as reflecting one specific code branch.
- Files modified:
02-FINDINGS.md(documentation only). - Committed in:
48a2ff7c(Task 3 commit).
Total deviations: 1 auto-fixed (Rule 1, three files sharing one bug class), 2 scope deviations (both investigated and documented, neither silently dropped or silently expanded into new file territory). Impact on plan: The plan's own success criteria are met for three of six surfaces (fixed, real numbers proving it); the other three each have an honest, evidenced account (restored-to-baseline-with-pre-existing-residual, a named-but-out-of-scope second cause, and a not-measurable-this-pass note) rather than a shrug or a silently-inflated "complete" claim.
Issues Encountered
- A concurrent
cargo build --release/cargo test -p archipelagoon this shared box (another session's FED-08 work) required waiting for CPU contention to clear before taking any timed measurement — confirmed viapgrep -af 'cargo|rustc'before each measurement pass, per this plan's own precondition. - The coordinator intervened mid-plan with three follow-up directives after the first re-measure came back: (1) re-investigate OpenWrtGateway rather than continuing to harden its navigation selectors, (2) profile Discover specifically for a second cause, (3) report both the regression-closed bar and the <300ms target bar separately per surface. All three addressed as directed, in this same session, inline (no further background-task waiting).
package-lock.jsonshowed an unrelated, unstaged diff (npm's own routine"peer": truemetadata normalization, triggered by a localnpminvocation) — left untouched and unstaged, per the shared-tree rule to stage only exact paths this plan edited.
User Setup Required
None for this plan's own execution — ARCHY_PASSWORD was supplied out-of-band by the coordinator at runtime, passed only as an environment variable, never written to any file, commit, or this summary.
Next Phase Readiness
- Phase 02's last open verification item (six confirmed timing regressions, none previously fixed) now has: three real fixes proven on real hardware, two honest restored-to-baseline-with-named-residual accounts, and one honestly-unresolved-but-evidenced second cause with a clear scoped destination for follow-up work.
- Carried forward, not resolved: Discover's CSS-entrance-animation-replay defect needs a dedicated follow-up plan touching Apps.vue/Marketplace.vue/Home.vue/four Web5 sub-card files, with its own real-device visual-verification checkpoint — tracked in
STATE.md's Blockers/Concerns, not silently dropped. - No blockers for a
gsd-verifierre-run on Phase 02 from this plan specifically; the phase's own top-level checkbox inROADMAP.mdis deliberately left unchecked pending that verification pass's own judgment on whether Discover's outstanding item blocks phase completion or is an acceptable scoped-out follow-up.
Phase: 02-ui-performance Completed: 2026-07-31