diff --git a/.planning/phases/02-ui-performance/02-12-PLAN.md b/.planning/phases/02-ui-performance/02-12-PLAN.md index 1d11b3e1..83592977 100644 --- a/.planning/phases/02-ui-performance/02-12-PLAN.md +++ b/.planning/phases/02-ui-performance/02-12-PLAN.md @@ -37,6 +37,32 @@ must_haves: - "02-PERF-CARD-STAGGER.json -> 02-PERF-FINAL.json (02-11) — same four surfaces, same harness, joined by surface `id`" --- +# CANCELLED + +**Cancelled 2026-07-31 by Dorian's explicit decision: "no changing animations allowed."** + +This plan is not being implemented, now or later. Entrance-animation behavior is +Dorian's domain and is not to be changed, including the specific fix proposed below +(clearing the `card-stagger`/`home-card-animate` class on KeepAlive deactivation so it +doesn't replay on reactivation). No source files were changed under this plan — the +one file that was written during investigation (`neode-ui/src/composables/useEntranceStagger.ts`, +never imported/wired into anything) was deleted before this cancellation was recorded. + +**The underlying replay is understood and documented, not lost.** The investigation +that produced this plan, plus a follow-up read-only investigation performed after +Dorian's cancellation decision arrived mid-execution, are preserved below and in the +`## Investigation Findings (preserved — do not re-investigate)` section at the end of +this file, specifically so nobody re-does this analysis from scratch. `02-FINDINGS.md` +and `REQUIREMENTS.md` are unchanged by this cancellation — Discover's revisit cost +stays exactly as measured in 02-11 (1389ms FINAL), recorded as a formally accepted +deviation on Dorian's authority, not a defect awaiting a future fix. + +Everything below this notice (objective, tasks, threat model, etc.) is the plan AS +PROPOSED, kept verbatim for the record. It was never executed past Task 1 (which +itself was rolled back — see the preserved findings section). + +--- + 02-11 profiled Discover's revisit cost directly (not guessed) and found a SECOND, distinct cause beyond the three leaked pollers it fixed: the `card-stagger`/`home-card-animate` entrance-animation classes are baked into the DOM at first mount and never programmatically removed. Chromium restarts `animation-fill-mode:forwards` CSS animations on DOM reattachment — independent of Vue's own reactivity — so every KeepAlive detach/reactivate cycle replays the entrance animation on a revisit, even though the JS-level "once per session" flag governing each view is itself correct and untouched. 02-11 named this cause with full profiling evidence (a diagnostic showing Discover's card count transiently doubling 19->34->19 on every single revisit, and an `animationstart`/`animationend` event log spanning the whole revisit window) but deliberately left it unfixed — the blast radius (Discover.vue, Apps.vue, Marketplace.vue, Home.vue, and several Web5 sub-cards) exceeded that plan's scope and needed its own real-device verification budget. @@ -263,3 +289,77 @@ On archi-dev-box (`http://archi-dev-box`), in a fresh browser session (private/i Create `.planning/phases/02-ui-performance/02-12-SUMMARY.md` ONLY after Task 4's checkpoint is approved — per-file before/after description of the fix, the regression tests and their pass/fail-before proof, the re-measured numbers for all four surfaces, confirmation of the human verification outcome, and confirmation the visual/animation and Home-overlay constraints held throughout. + +--- + +## Investigation Findings (preserved — do not re-investigate) + +Recorded 2026-07-31, after Dorian's cancellation decision arrived mid-execution (Task 1's +composable had been drafted, uncommitted, and was deleted; no other task ran). Kept here +so a future session doesn't have to re-derive any of this from scratch, even though the +fix itself is not to be implemented. + +**1. Exact blast radius (verified by direct grep of `neode-ui/src`, not just trusting +02-11's own list — confirmed accurate, nothing to correct):** + +Exactly 5 files own a flag: `Discover.vue` (`discoverAnimationDone` / `showStagger`), +`Apps.vue` (`appsAnimationDone`), `Marketplace.vue` (`marketplaceAnimationDone`), +`web5/Web5.vue` (`web5AnimationDone`), `Home.vue` (`animateCards`, a `ref` that only +ever flips `true` and never back — same structural defect, different variable shape). +Nine more files consume the flag as a plain boolean prop with no logic of their own: +`discover/AppGrid.vue`, `discover/FeaturedApps.vue`, `apps/AppCard.vue`, +`marketplace/MarketplaceAppCard.vue`, and the Web5 sub-cards `Web5Wallet.vue`, +`Web5Identities.vue`, `Web5NodeVisibility.vue`, `Web5NostrRelays.vue`, +`Web5QuickActions.vue`, `Web5SharedContent.vue`, `Web5Domains.vue`. Checked for sibling +entrance-animation classes with the same defect (`.animate-fade-up`/`.animate-fade-in`/ +`.animate-fade-out` in `style.css`): none apply to any KeepAlive'd tab — they're used +only by `RootRedirect.vue`/`OnboardingIntro.vue`, neither of which is ever cached, so +neither is affected. + +**Nuance not in 02-11:** Discover has no sidebar entry — the perf harness's own +`navSteps` (and the real user path) reach it via `[click "Apps" in sidebar, click +"App Store" tab inside Apps]`. `measure.ts`'s revisit timer starts at the *first* +click (into Apps) and stops when Discover's content is visible, so the measured +1389ms "Discover" number already includes Apps.vue's own reactivation (and Apps' +own `card-stagger` replay, if its installed-app count is large enough to matter) — +a fix confined to Discover.vue alone would not fully address the measured number; +Apps.vue would need to be fixed too, which the (now-cancelled) plan already accounted +for. + +**2. What a user sees today on a revisit:** + +A full restart of the entrance cascade, not a partial or subtle effect — clearly +perceptible, not sub-perceptual jank. Each card is `opacity:0` at rest with +`animation: card-stagger-in 0.4s ... forwards; animation-delay: calc(var(--stagger-index) * 50ms)`. +Discover's default view (category "all", no search) computes `--stagger-index` as the +card's own `v-for` index + a 4-card offset; 02-11's diagnostic recorded 19 settled +cards, so indices run roughly 4–22, giving a last-card delay of ~1.1s + its own 0.4s +animation — **the full cascade takes ~1.5s from first card to last**, matching the +diagnostic's own directly-observed `animationstart`/`animationend` event log spanning +**+241ms to +1716ms**. This replays in full on every single revisit of the App Store +tab. + +**3. Whether the animation is the whole of Discover's residual cost, or only part:** + +Best available evidence says it's the dominant cost specifically for Discover, though +this was never isolated with a controlled A/B measurement (that would have been +Task 3's job). Supporting evidence: 02-11's own diagnostic recorded first-paint at +901ms against a content-visible wall-clock of 1095ms in that run — almost the entire +visible window was animation-driven paint, not RPC or compute (a CPU profile +separately showed 86–99% idle/program time on this and every other regressed surface, +ruling out heavy JS computation). Calibration point: Apps.vue's own revisit already +lands at 184–267ms with the identical code pattern (presumably a much smaller +installed-app count, so its own cascade is short even though it still technically +replays) — by analogy, removing the replay was projected to bring Discover's revisit +down substantially, plausibly into the same low-hundreds-of-ms range, though the exact +number was never measured (Task 3 never ran). One more calibration point: pre-KeepAlive, +every visit to Discover was a full remount, so the entrance animation legitimately +played on every visit back then too — the ~1083ms pre-phase-2 baseline itself likely +already contained this same ~1.5s cascade, which is plausibly why baseline was already +slow. What KeepAlive changed was the *intent* (revisits should skip re-render +entirely) without this animation-gating code ever being updated for that new contract. + +**Disposition:** Accepted deviation, on Dorian's explicit authority (entrance-animation +behavior is his domain). Discover's revisit cost stays at its 02-11 FINAL measurement +(1389ms) indefinitely, or until Dorian himself decides otherwise. Not a defect awaiting +a future fix.