24 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-ui-performance | 08 | execute | 5 |
|
|
false |
|
|
Purpose: PERF-01 closes the loop it opened — the same instrument, the same target, before and after. PERF-02 and PERF-03 are both stated in terms of what the user perceives on real node hardware, and CONTEXT.md D-11 makes archi-dev-box the verification target with an explicit pass bar: no visible spinner or blank on revisit of a tab or secondary screen already visited this session; first visits may still show loading. D-15 keeps this to the dev pair — no OTA, no fleet.
Output: a committed after-artifact, a per-surface before/after comparison including any regression, an instance-cache cap set from observed memory, and a human-confirmed pass bar on the node.
<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-ui-performance/02-CONTEXT.md @.planning/phases/02-ui-performance/02-FINDINGS.md @.planning/phases/02-ui-performance/02-02-SUMMARY.md @.planning/phases/02-ui-performance/02-03-SUMMARY.md @.planning/phases/02-ui-performance/02-04-SUMMARY.md @.planning/phases/02-ui-performance/02-05-SUMMARY.md @.planning/phases/02-ui-performance/02-06-SUMMARY.md @.planning/phases/02-ui-performance/02-07-SUMMARY.md @CLAUDE.md Task 1: Build and deploy the frontend to the dev pair only archi-dev-box resolves and answers over HTTP from this machine, and `scripts/deploy-config.sh` exists (it is gitignored; `scripts/deploy-config.example` documents it) so the deploy script can authenticate neode-ui/src/views/dashboard/keepAliveRoutes.ts - `CLAUDE.md` — the build note: `neode-ui/` builds to `web/dist/neode-ui/`, and the built bundle must be grepped for new strings before shipping because the build can silently no-op. Also the commit-and-push-every-unit-of-work rule. - `scripts/deploy-to-target.sh` lines 1-30 — the usage block. `--frontend-only` skips the Rust build and container rebuilds; `--live` targets the default host; `--both` fans out to additional hosts; `--tailscale` reaches the alpha-tester nodes. - `scripts/deploy-config.example` — what `deploy-config.sh` must contain. - `.planning/PROJECT.md` — the deploy-to-the-dev-pair-before-any-OTA rule. - `.planning/phases/02-ui-performance/02-CONTEXT.md` — D-15 restricts this phase to the dev pair with no OTA, and D-11 names archi-dev-box as the verification target. - `.planning/phases/02-ui-performance/02-0{2,3,4,5,6,7}-SUMMARY.md` — the list of new symbols and cache keys to grep the built bundle for. Run the full check suite from `neode-ui/` first: `npm run type-check`, then `npm run test`, then `npm run build`. All three must be green before anything ships.Then confirm the build is real, not a silent no-op. CLAUDE.md warns about exactly this.
Grep `web/dist/neode-ui/` for a representative string introduced by each plan in this
phase — the `shouldKeepAlive` classifier, the `RefreshIndicator`, a cache key such as
`app-catalog` or `app-details:`, and the D-14 flag names recorded in
`02-AIUI-D14.md`. Collect the exact strings from the plan SUMMARYs rather than
guessing them. If any is absent, the build did not take — clean and rebuild before
deploying, and record what happened.
Deploy the frontend to the dev pair with `scripts/deploy-to-target.sh
--frontend-only`, targeted at the dev pair hosts only. Read the script's usage block
and its host configuration to identify which flag combination reaches exactly the dev
pair. Do not use `--tailscale` or `--tailscale-node` — those reach alpha-tester fleet
nodes. Do not trigger any OTA or release path. D-15 is explicit and this plan's third
prohibition restates it. Record the exact command run and the hosts it touched.
After deploying, tune the instance-cache cap. `KEEP_ALIVE_MAX` has been 6 since plan
02-02 on the reasoning that it is smaller than the main-tab count so the long tail
evicts, which was never validated against real hardware. On archi-dev-box, open the
UI, cycle through every main tab twice including Mesh, and read the browser's memory
usage before and after. If resident memory grows in a way that would matter on a
low-power fleet node, lower the cap; if it is comfortably flat and evictions are
causing visible reloads of tabs the user is actively cycling, raise it. Change the
constant only if the observation calls for it, commit the change, and record the
measurement either way — an unchanged 6 with a recorded memory reading is a valid and
preferable outcome to an unexamined 6.
Commit and push each unit of work as it lands, per CLAUDE.md, staging explicitly by
path.
Append a `## Results` section to `02-FINDINGS.md` with a table pairing each surface's
baseline and after rows: Surface, Baseline revisit ms, After revisit ms, Baseline
revisit RPC count, After revisit RPC count, Baseline remounted, After remounted,
Verdict. `Verdict` takes one of `improved`, `unchanged`, `regressed` or `unmeasured`.
Three rules govern that table and none of them may be softened:
- A surface that got worse is recorded as `regressed` with its numbers. Do not average
it into an aggregate, do not re-run until it looks better, do not drop it.
- A surface that could not be measured is `unmeasured` with the reason. It is never
recorded as `improved`.
- Every number comes from the artifacts. No number is estimated, inferred from reading
the code, or taken from the best of several runs.
Add a `## Outstanding` subsection listing anything still open: surfaces still
classified `unmeasured`, any secondary screen plan 02-03 reported as a gap rather than
converting, any `regressed` verdict, and any assumption from the plan set's
`Assumptions & Flagged Items` blocks that execution did not settle. This list is what
`/gsd-verify-work` and any follow-up gap-closure planning read.
Redact before committing, as in plan 02-01: onion addresses, DIDs, pubkeys, wallet
figures, peer hostnames and file names do not go into the artifacts. RPC method names
and timings do.
1. Open archi-dev-box's UI directly on the node (not the local dev preview) so you are
exercising the deployed build on real hardware.
2. First pass — visit every main tab once, in order: Home, Apps, App store, Cloud,
Mesh, Server, Web5, Fleet, Chat, Settings. Loading here is expected.
3. Second pass — revisit each of those tabs in a different order. Expected on every
one: content appears immediately, no spinner, no blank frame, scroll position and
in-page state preserved, no intro animation replay.
4. Open at least three secondary screens: an app's detail page, a cloud folder, and
one more of your choosing. Go back to the parent tab and reopen each. Expected on
reopen: content appears immediately with no blocking reload.
5. The app store specifically — this is the surface you reported as worst. Switch into
it and out of it several times. Expected: it should feel immediate every time after
the first.
6. Home wallet — note the balance, go away for a minute, come back. Expected: the
figure is there instantly and visibly re-checks (a small indicator, then the
current number). It must not sit frozen.
7. Mesh — enter, let the graph settle, leave, return. Expected: the graph is where you
left it, the map draws correctly, and the layout does not re-animate from scratch.
8. Chat — the AIUI panel should already be expanded, and should still be loaded after
switching away and back.
9. Extended use — keep using the node normally for several minutes, cycling tabs.
Expected: it stays fast. If sluggishness creeps back after extended use, say so and
name the tabs you had visited — that points at the instance-cache cap.
10. Compare against how it felt before this phase. The question that decides this
checkpoint: is the sluggishness you reported gone on this device?
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| developer workstation → dev-pair nodes | A build crosses onto running hardware via the deploy script |
| dev pair → fleet / OTA channel | The boundary D-15 forbids crossing in this phase |
| archi-dev-box measurements → committed artifacts | Node data crosses into a pushed repository |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-02-21 | Elevation of Privilege | scripts/deploy-to-target.sh reaching fleet or alpha-tester nodes |
high | mitigate | Task 1 requires --frontend-only targeted at the dev pair, explicitly forbids --tailscale and --tailscale-node, forbids any OTA or release path, and requires the exact command and host list to be recorded in the SUMMARY for audit |
| T-02-06 | Information Disclosure | 02-PERF-AFTER.json and 02-FINDINGS.md committed to a pushed repo |
medium | mitigate | Task 2 carries the same redaction rule as plan 02-01: RPC method names and timings only, no addresses, identities, balances or file names |
| T-02-22 | Repudiation | An unverified build shipped and later believed to contain this phase's changes | medium | mitigate | Task 1 greps the built bundle for a representative string from every plan in the phase before deploying, per the CLAUDE.md silent-no-op warning, and Task 2's precondition re-greps the asset actually served by the node |
| T-02-03 | Denial of Service | Instance-cache memory on low-power hardware | medium | mitigate | Task 1 sets KEEP_ALIVE_MAX from an on-device memory reading and records the reading; Task 3 step 9 exercises extended use to surface any residual growth |
| T-02-SC | Tampering | npm/pip/cargo installs | high | mitigate | No package-manager installs are in scope; this plan builds and deploys existing code. A task that finds it needs a new dependency stops and routes through the Package Legitimacy Gate with a blocking human checkpoint before installing |
| </threat_model> |
<artifacts_this_phase_produces>
Artifacts this phase produces
Created or changed by this plan — new API, not drift:
.planning/phases/02-ui-performance/02-PERF-AFTER.json.planning/phases/02-ui-performance/02-FINDINGS.md— gains## Resultsand## OutstandingKEEP_ALIVE_MAX— value finalised from an on-device memory reading
Full phase inventory (for the source-grounding pass): neode-ui/e2e/perf/surfaces.ts
(SURFACES, Surface), neode-ui/e2e/perf/measure.ts (measureSurface,
SurfaceMeasurement, RpcCall), neode-ui/e2e/perf/surface-perf.spec.ts,
neode-ui/src/views/dashboard/keepAliveRoutes.ts (shouldKeepAlive, KEEP_ALIVE_PATHS,
KEEP_ALIVE_MAX), neode-ui/src/views/dashboard/DashboardRouterView.vue
(isFullBleedRoute, wrapperClass, wrapperStyle),
neode-ui/src/components/RefreshIndicator.vue, resources.clearAll(), TAB_ORDER
(promoted to an export of useRouteTransitions.ts),
neode-ui/src/composables/__tests__/useCachedResource.test.ts,
neode-ui/src/views/dashboard/__tests__/keepAliveTabs.test.ts,
neode-ui/src/views/dashboard/__tests__/keepAliveLifecycle.test.ts,
neode-ui/src/views/__tests__/{secondaryScreenCache,meshTabCache,serverTabCache,homeTabCache,chatAiuiEmbed}.test.ts,
neode-ui/src/stores/__tests__/resourcesClear.test.ts,
.planning/phases/02-ui-performance/{02-FINDINGS.md,02-PERF-BASELINE.json,02-PERF-AFTER.json,02-AIUI-D14.md},
cache keys app-catalog, bitcoin.prune-status, app-details:<dataset>:<id> and the
per-group Mesh, Server and Home keys recorded in their plan SUMMARYs, environment variable
ARCHY_PERF_OUT, and the two AIUI embed query parameters named in 02-AIUI-D14.md.
</artifacts_this_phase_produces>
<assumptions_and_flagged_items>
Assumptions & Flagged Items
Edge-coverage probe rows (spec-less fallback — all three unclassified/unresolved)
| Requirement | Probe status | Disposition here |
|---|---|---|
| PERF-01 | unclassified / unresolved |
FLAGGED, not auto-backstopped, not dropped. Resolved in substance by this plan's before/after comparison truths and by plan 02-01's measurement truths. The probe row itself stays unresolved and is surfaced for human review. |
| PERF-02 | unclassified / unresolved |
FLAGGED. Resolved in substance by the D-11 pass-bar truths here and by the per-tab truths in plans 02-02, 02-04, 02-05, 02-06 and 02-07. |
| PERF-03 | unclassified / unresolved |
FLAGGED. Resolved in substance by the secondary-screen pass-bar truth here and by the per-item cache truths in plan 02-03. |
The extended-use stability truth is carried as a verification: backstop marker: it is a
perceptual and long-running property that neither the unit suite nor a single harness run
can confirm, so it abstains to human review rather than passing silently.
Carried assumptions this plan is the last chance to settle
- FA-D (
KEEP_ALIVE_MAX): carried at 6 through plans 02-02 to 02-07 on reasoning alone. Task 1 replaces the estimate with an on-device memory reading. If the reading is not taken, the assumption stays open and belongs in## Outstanding. - FA-B (
ContainerAppDetails.vue): plan 02-01 records the reachability verdict and plan 02-03 excludes the file. If the verdict is that it is dead code, note in## Outstandingthat removing it is a candidate for a follow-up cleanup, not part of this phase. - FA-E (AIUI source): if plan 02-07's Task 2 precondition halted, D-14 is incomplete and belongs in
## Outstandingas a blocked locked decision — not as a deferred one. - Scope gaps from plan 02-03: any secondary screen the findings named that plan 02-03 reported rather than converted is listed in
## Outstandingwith its measured cause. </assumptions_and_flagged_items>
<success_criteria>
- Every surface the findings named as slow has a committed after-number from the same instrument and the same target
- Every registered main tab shows a surviving instance in the after-artifact's remount probe
- Revisits to tabs and secondary screens on archi-dev-box show no spinner and no blank screen
- Any regression or unmeasured surface is recorded as such, not smoothed over
- The instance-cache cap is justified by an observed memory reading
- The build reached the dev pair and nothing else </success_criteria>