docs(02-11): complete plan — leaked-poller fix, four-way verdicts, SUMMARY
Phase 02's last open item closed: web5/server/fleet fixed and proven on archi-dev-box; app-details restored to at/near baseline; discover's second cause (CSS entrance-animation replay on KeepAlive reactivation) named and evidenced but not fixed, scoped as a follow-up; openwrt-gateway not measurable this pass, prior numbers flagged not retracted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
68d63b1140
commit
f514ab515b
+19
-15
@@ -5,16 +5,16 @@ milestone_name: milestone
|
||||
current_phase: 02
|
||||
current_phase_name: ui-performance
|
||||
status: verifying
|
||||
stopped_at: Completed 02-10-PLAN.md — Phase 02 (ui-performance) gap closure complete, both verification gaps resolved
|
||||
last_updated: "2026-07-31T10:57:35.534Z"
|
||||
stopped_at: Completed 02-11-PLAN.md — Phase 02 (ui-performance) final gap closure: three leaked pollers fixed and re-measured (web5/server/fleet), Discover's second cause named (not fixed, scoped as follow-up), openwrt-gateway data-integrity note recorded
|
||||
last_updated: "2026-07-31T13:45:00.000Z"
|
||||
last_activity: 2026-07-31
|
||||
last_activity_desc: 02-09 gap closure complete
|
||||
last_activity_desc: 02-11 gap closure complete — leaked background pollers fixed, four-way re-measure verdicts written
|
||||
progress:
|
||||
total_phases: 9
|
||||
completed_phases: 2
|
||||
total_plans: 27
|
||||
completed_plans: 17
|
||||
percent: 22
|
||||
total_plans: 28
|
||||
completed_plans: 18
|
||||
percent: 23
|
||||
---
|
||||
|
||||
# Project State
|
||||
@@ -28,12 +28,12 @@ See: .planning/PROJECT.md (updated 2026-07-29)
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 02 — gap closure in progress
|
||||
Plan: 10 of 10 (02-09 complete; 02-10 running concurrently)
|
||||
Status: 02-09 complete (checkpoint approved); 02-10 in progress
|
||||
Last activity: 2026-07-31 — 02-09 gap closure complete
|
||||
Phase: 02 — gap closure complete (02-11 was the last open item)
|
||||
Plan: 11 of 11 complete
|
||||
Status: 02-11 complete — three leaked background pollers fixed and re-measured; Discover's second cause named, not fixed (scoped follow-up); openwrt-gateway unmeasurable this pass (browser crash), prior numbers stand with a data-integrity note
|
||||
Last activity: 2026-07-31 — 02-11 gap closure complete
|
||||
|
||||
Progress: [██████░░░░] 63%
|
||||
Progress: [███████░░░] 67%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -62,6 +62,7 @@ Progress: [██████░░░░] 63%
|
||||
| Phase 02 P08 | ~190min | 3 tasks | 4 files |
|
||||
| Phase 02 P09 | 130min | 3 tasks | 3 files |
|
||||
| Phase 02 P10 | 55min | 2 tasks | 3 files |
|
||||
| Phase 02 P11 | ~150min | 3 tasks | 8 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -114,6 +115,9 @@ Decisions are logged in PROJECT.md (10 locked ADRs in the `<decisions>` block +
|
||||
- [Phase ?]: 02-09: /dashboard/server's (and Web5's) 'genuinely remounts' reading was a proven probe-measurement artifact (generic .view-container selector can't disambiguate the foreground tab from other still-connected cached tabs) — confirmed via document.elementFromPoint() hit-test contradicting the naive verdict across device runs; no source change needed, pinned with vm.$.uid-based regression tests instead
|
||||
- [Phase ?]: 02-09: committed neode-ui/e2e/perf/keepalive-remount-probe.spec.ts as a re-runnable, instrumented probe covering every KEEP_ALIVE_PATHS tab, replacing the ad-hoc 02-08 probe so this class of false positive cannot recur
|
||||
- [Phase ?]: [Phase 2, gap closure 02-10]: Wallet/send-flow's timing regression cleared as environmental noise (re-measure at/below baseline); Discover/Server/Web5/AppDetails/OpenWrtGateway confirmed as real, phase-2-caused client-side render/reactivation regressions via 3-run dispersion + git bisection, recorded as accepted deviations (not fixed — deploy blocked mid-session by a shared-tree hazard with concurrent security-follow-up and BotFights sessions)
|
||||
- [Phase 2, gap closure 02-11]: Real cause of the six regressions was NOT compute-bound render cost (CPU profile: 86-99% idle/program, <10% JS self-time everywhere) — it was three background pollers (useFleetData.ts 60s, FipsNetworkCard.vue 15s, Web5Monitoring.vue 30s) armed in onMounted and never disarmed once their owning views joined KEEP_ALIVE_PATHS in 02-04, invisible to that audit because it grepped the top-level view files, not the child composables they delegate to. Gated to onActivated/onDeactivated, mirroring 02-04's own established pattern. Fixed: web5 275ms (was 566ms baseline/1329ms regressed), server 574ms (was 738/1239), fleet 790ms (was 330/2631)
|
||||
- [Phase 2, gap closure 02-11]: Discover (1389ms, worst remaining) has a SECOND, distinct cause: card-stagger/showStagger entrance-animation classes are baked into the DOM at first mount and never programmatically removed, so every KeepAlive detach/reattach cycle restarts the CSS animation on reactivation — replaying the full entrance cascade on every revisit. Confirmed via a diagnostic (DOM card count doubling transiently on every revisit) and an extended animation-event log. NOT fixed — blast radius spans 5+ files outside 02-11's scope (Apps.vue, Marketplace.vue, Home.vue, several Web5 sub-cards), needs its own real-device verification budget; recommended as a dedicated follow-up
|
||||
- [Phase 2, gap closure 02-11]: openwrt-gateway unmeasurable in the final re-measure (Chromium "Target crashed" cascading from an unrelated surface, cloud-folder, earlier in the same harness run) — recorded as not-measurable, not written in as data. Separately confirmed the prior baseline/after/remeasure numbers were measuring a real, substantive disconnected-state UI (OpenWrtGateway.vue's h1 is unconditional; a "No router configured" RPC error deterministically renders a real Connect-to-Router form, not a blank/error page) — the six-surface regression count is not retracted, but the numbers reflect one specific code branch (no OpenWrt device has ever been connected to archi-dev-box)
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -134,8 +138,8 @@ Decisions are logged in PROJECT.md (10 locked ADRs in the `<decisions>` block +
|
||||
- [Global] Live OTA fleet: deploy to the dev pair before any OTA; gate re-runs required after orchestrator changes; some verification is user/hardware-gated (radios, on-device tests)
|
||||
- cloud.ts's navigate() needs a TTL gate to fully satisfy 'no new RPC within TTL' for CloudFolder.vue — currently always re-fetches on revisit (just doesn't block paint)
|
||||
- [Phase 2, RESOLVED by 02-09] ~~Server.vue does not survive a tab round-trip despite KEEP_ALIVE_PATHS registration~~ — retracted: proven a probe-measurement artifact (shared generic `.view-container` selector couldn't disambiguate the foreground tab from other cached tabs), not a real defect. Server.vue's (and Web5.vue's) instance genuinely survives; pinned with `vm.$.uid`-based regression tests immune to the same ambiguity. Checkpoint approved on real hardware.
|
||||
- [Phase 2, carried forward] Timing regressions on Discover/Web5/Fleet/AppDetails/OpenWrtGateway (02-PERF-AFTER.json vs baseline) — a same-time-of-day re-run would help disambiguate real regression from node-load noise
|
||||
- [Phase 2, carried forward] Discover/Server/Web5/AppDetails/OpenWrtGateway/Fleet all show a confirmed, growing, phase-2-caused client-side render/reactivation revisit-ms regression (02-10's re-measure verdict) — needs DevTools Performance-panel profiling to pinpoint the mechanism, plus a clean deploy window once the concurrent security-follow-up/BotFights sessions land their own work
|
||||
- [Phase 2, RESOLVED by 02-11] ~~Timing regressions on Discover/Web5/Fleet/AppDetails/OpenWrtGateway~~ — root cause found (three leaked background pollers, not compute-bound render cost) and fixed for web5/server/fleet, each proven with a real before/after number on archi-dev-box. AppDetails restored to at/near its own pre-phase-2 baseline (pre-existing per-mount cost, not a new defect). OpenWrtGateway not measurable this pass (browser crash); prior numbers stand with a data-integrity note (measuring a real disconnected-device UI, not an empty page).
|
||||
- [Phase 2, follow-up needed] Discover (1389ms, worst remaining named surface) has a second, evidenced, phase-2-caused defect: KeepAlive'd entrance-stagger animations (`card-stagger`/`showStagger`) never get their class removed from the DOM after first play, so every reactivation replays the full CSS animation cascade. Fix requires touching 5+ files outside 02-11's scope (Apps.vue, Marketplace.vue, Home.vue, Web5Wallet.vue/Web5Identities.vue/Web5NodeVisibility.vue/Web5NostrRelays.vue) with its own real-device visual-regression verification budget (the same class of risk 02-02's original KeepAlive rollout hit on its first checkpoint attempt) — needs a dedicated follow-up plan, not squeezed into a gap-closure pass.
|
||||
|
||||
### Quick Tasks Completed
|
||||
|
||||
@@ -157,6 +161,6 @@ Decisions are logged in PROJECT.md (10 locked ADRs in the `<decisions>` block +
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-07-31T10:57:35.509Z
|
||||
Stopped at: Completed 02-10-PLAN.md — Phase 02 (ui-performance) gap closure complete, both verification gaps resolved
|
||||
Last session: 2026-07-31T13:45:00.000Z
|
||||
Stopped at: Completed 02-11-PLAN.md — Phase 02 (ui-performance) final gap closure complete
|
||||
Resume file: None
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
phase: 02-ui-performance
|
||||
plan: 11
|
||||
subsystem: ui
|
||||
tags: [playwright, cdp-profiling, keepalive, background-pollers, css-animation, gap-closure]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 02-ui-performance/02-10
|
||||
provides: "The three-way dispersion confirmation that Discover/Server/Web5/AppDetails/OpenWrtGateway/Fleet are real, phase-2-caused revisit-ms regressions (not noise), each recorded as an accepted deviation pending a profiling-then-fix pass"
|
||||
provides:
|
||||
- "neode-ui/e2e/perf/profile-revisit.spec.ts — additive CDP Profiler+Tracing diagnostic (CPU self-time by chunk, Chrome trace event breakdown, setTimeout/rAF instrumentation, CSS transition/animation event log, independent first-paint probe) for the six named surfaces, frozen harness untouched"
|
||||
- "02-FINDINGS.md § Client-Side Render Cost Root Cause + § Task 3 — the real, profiled cause named before any fix, three fixes landed with before/after numbers, a second cause found and evidenced (not fixed), and an OpenWrtGateway data-integrity correction to the record"
|
||||
- "02-PERF-FINAL.json — fourth on-device measurement (5 runs/surface) against the fixed build"
|
||||
affects: []
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "CDP Profiler (CPU sampling) + Tracing (devtools.timeline categories) combined in one Playwright session to distinguish compute-bound cost (attributed JS self-time) from rendering-pipeline/network-wait cost (idle/program-dominated, layout/paint/recalc-style named separately) — the same underlying data DevTools' Performance panel visualizes"
|
||||
- "Runtime setTimeout/requestAnimationFrame monkey-patch (installed/restored per measurement window) to attribute a large idle-time share to a concrete scheduled delay vs. an animation-frame chain vs. neither (network wait)"
|
||||
- "document-level transitionrun/transitionend/animationstart/animationend capture (capture phase) to separate first-paint (independent rAF-driven bounding-box poll) from the harness's own stricter contentSelector-visible field — Playwright's 'visible' state never gates on opacity/transform, only non-empty bounding box + not visibility:hidden, so a slow CSS transition doesn't block the measured field but does explain what a human perceives"
|
||||
- "Extending 02-04's per-view lifecycle audit to child components/composables a view delegates to (not just the top-level view file) — the exact gap that let three setInterval leaks through 02-04's own grep"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- neode-ui/e2e/perf/profile-revisit.spec.ts
|
||||
- .planning/phases/02-ui-performance/02-PERF-FINAL.json
|
||||
modified:
|
||||
- .planning/phases/02-ui-performance/02-FINDINGS.md
|
||||
- .planning/REQUIREMENTS.md
|
||||
- .planning/STATE.md
|
||||
- .planning/ROADMAP.md
|
||||
- neode-ui/src/views/fleet/useFleetData.ts
|
||||
- neode-ui/src/views/server/FipsNetworkCard.vue
|
||||
- neode-ui/src/views/web5/Web5Monitoring.vue
|
||||
- neode-ui/src/views/dashboard/__tests__/keepAliveLifecycle.test.ts
|
||||
|
||||
key-decisions:
|
||||
- "Named the cause with real profiling evidence before any source-file change (D-10): a CDP CPU profile showed 86-99% of every revisit window in (idle)/(program) with under 10% genuine JS self-time on every one of the six surfaces — ruling out expensive computed re-evaluation, watcher cascades, and whole-subtree re-renders (all of which would show as real, attributed JS self-time) as the dominant cost, redirecting the investigation toward background contention rather than render cost"
|
||||
- "Extended 02-04's lifecycle-audit grep (setInterval present without a matching onActivated/onDeactivated pair) past the top-level view files it originally checked, into every child component/composable a KeepAlive'd view delegates to — found three offenders (useFleetData.ts, FipsNetworkCard.vue, Web5Monitoring.vue) invisible to the original per-view audit"
|
||||
- "Fixed all three leaked pollers with the exact onActivated/onDeactivated arm/disarm pattern 02-04 already established and proved safe for Server.vue's own vpnPollInterval — no new pattern invented"
|
||||
- "RED-before-GREEN discipline on the three regression tests: ran them against the pre-fix code via git stash (not assumed), confirmed all three fail in the expected direction (kept firing while deactivated), then restored the fix and confirmed all three pass"
|
||||
- "Discover's second, distinct cause (CSS entrance-animation replay on KeepAlive reactivation — card-stagger/showStagger classes baked into the DOM at first mount and never programmatically removed) was found, evidenced with a direct diagnostic and profiling data, and NOT fixed — the safe fix's blast radius (5+ files: Apps.vue, Marketplace.vue, Home.vue, four Web5 sub-card files) exceeds this plan's declared scope and needs its own real-device visual-verification budget, matching the exact class of risk 02-02's original KeepAlive rollout hit on its first checkpoint attempt. Named as a dedicated follow-up rather than expanded into this plan under time pressure, per the coordinator's own explicit instruction not to open-endedly optimize"
|
||||
- "OpenWrtGateway's final re-measure returned no data (a Chromium 'Target crashed' error cascading from an unrelated surface, cloud-folder, earlier in the same harness run) — recorded as not-measurable, never written in as data. Separately investigated and confirmed via source (OpenWrtGateway.vue's h1 is unconditional; a 'No router configured' RPC error deterministically renders a real Connect-to-Router form) that the prior baseline/after/remeasure numbers were measuring a genuine, substantive disconnected-state UI render, not an empty/error page — the six-surface regression count is not retracted on this basis, but the numbers are flagged as reflecting one specific code branch (no OpenWrt device has ever been connected to archi-dev-box)"
|
||||
- "Never re-enabled sessionStorage persistence on any T-02-01 persist:false cache key, and never touched keepAliveTabs.test.ts, to shave milliseconds — confirmed via git diff --stat that the structural visual-contract test file is byte-for-byte unmodified throughout this plan"
|
||||
|
||||
requirements-completed: []
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "A real CPU/rendering profile (CDP Profiler + Tracing) captured for each of the six named surfaces' revisit window, named cause per surface cited to specific profile/trace/animation-log fields, committed before any neode-ui/src change"
|
||||
requirement: PERF-02
|
||||
verification:
|
||||
- kind: e2e
|
||||
ref: "neode-ui/e2e/perf/profile-revisit.spec.ts run against archi-dev-box; git diff --stat 3ee20430 confirms the frozen harness untouched"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "Every fixable named cause is fixed with a regression test that fails on the pre-fix code and passes after; no T-02-01 persist decision reverted; keepAliveTabs.test.ts byte-for-byte unmodified"
|
||||
requirement: PERF-02
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "keepAliveLifecycle.test.ts's new '02-11 gap closure' describe block, 3 tests; RED confirmed via git stash against pre-fix code, then GREEN restored"
|
||||
status: pass
|
||||
- kind: automated
|
||||
ref: "npm test (95 files/788 tests), npm run type-check, npm run build all green"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Deployed frontend-only to archi-dev-box, re-ran the frozen harness (5 runs/surface), and produced a four-way (Baseline/After/Remeasure/Final) verdict per named surface against both the phase-2-regression-closed bar and the <300ms target bar, reported separately"
|
||||
requirement: PERF-03
|
||||
verification:
|
||||
- kind: e2e
|
||||
ref: "02-PERF-FINAL.json, ARCHY_PERF_RUNS=5 against archi-dev-box; git diff --stat 3ee20430 confirms the frozen harness untouched"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
|
||||
duration: ~230min
|
||||
completed: 2026-07-31
|
||||
status: 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`) plus `STATE.md`/`ROADMAP.md` state-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 in `onMounted` and only disarmed it in `onUnmounted`/`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`/`onDeactivated` arm/disarm pattern 02-04 already established and proved safe for Server.vue's own `vpnPollInterval` — no new pattern invented. Added three regression tests to `keepAliveLifecycle.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 crashed` error 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 own `contentSelector` (`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`/`showStagger` entrance-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 extended `animationstart`/`animationend` event 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.
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1: Profile each surface's revisit window and name the real cause per surface** — `050a87d2` (feat)
|
||||
2. **Task 2: Fix each surface's named cause where fixable** — `2c25e512` (fix)
|
||||
3. **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 updated
|
||||
- `neode-ui/src/views/fleet/useFleetData.ts` — `armFleetPoll()`/`disarmFleetPoll()` gate the 60s poll to activate/deactivate
|
||||
- `neode-ui/src/views/server/FipsNetworkCard.vue` — `armFipsPoll()`/`disarmFipsPoll()` gate the 15s poll to activate/deactivate
|
||||
- `neode-ui/src/views/web5/Web5Monitoring.vue` — `armWeb5MonitoringPoll()`/`disarmWeb5MonitoringPoll()` gate the 30s poll to activate/deactivate
|
||||
- `neode-ui/src/views/dashboard/__tests__/keepAliveLifecycle.test.ts` — new `02-11 gap closure` describe 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.vue` each armed a `setInterval` in `onMounted` and only cleared it in `onUnmounted`/`onBeforeUnmount` — never-firing teardown hooks once their owning views joined `KEEP_ALIVE_PATHS` in 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 in `keepAliveLifecycle.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`/`showStagger` entrance 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 `showStagger` to 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 by `Apps.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's `files_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.md` Blockers/Concerns, and `REQUIREMENTS.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 crashed` error 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 archipelago` on this shared box** (another session's FED-08 work) required waiting for CPU contention to clear before taking any timed measurement — confirmed via `pgrep -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.json` showed an unrelated, unstaged diff** (npm's own routine `"peer": true` metadata normalization, triggered by a local `npm` invocation) — 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-verifier` re-run on Phase 02 from this plan specifically; the phase's own top-level checkbox in `ROADMAP.md` is 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*
|
||||
Reference in New Issue
Block a user