Files
archy/.planning/phases/02-ui-performance/02-10-PLAN.md
T

21 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, gap_closure, requirements, user_setup, must_haves
phase plan type wave depends_on files_modified autonomous gap_closure requirements user_setup must_haves
02-ui-performance 10 execute 7
02-09
.planning/phases/02-ui-performance/02-PERF-REMEASURE.json
.planning/phases/02-ui-performance/02-FINDINGS.md
true true
PERF-02
PERF-03
service why env_vars
archi-dev-box (the node's own UI login) The perf harness drives a real authenticated browser session against the deployed build on archi-dev-box — D-11's named on-device verification target, and the same target both prior measurement runs used. The password is not derivable from this environment.
name source
ARCHY_PASSWORD Supplied out-of-band by the coordinator at runtime, exactly as in 02-01 and 02-08. Passed only as an environment variable on the harness command line — never written to a file, never committed, never echoed into a log or a planning artifact.
truths artifacts key_links
Each of the six surfaces 02-VERIFICATION.md named (Discover, Server, Web5, AppDetails, OpenWrtGateway, wallet/send-flow) carries a written verdict backed by a three-artifact comparison — either cleared as environmental noise with the dispersion data that clears it, or confirmed as a real regression with its cause named
The environmental-noise theory 02-FINDINGS.md flagged but never tested is settled with data, not left as a caveat: the run conditions (clock time, disk usage, system load, whether the box was building) are recorded next to the numbers for the new run and stated for the two prior runs
No surface is left with an unexplained regression: each confirmed regression is either fixed (only when its cause traces to a phase-2 change and the fix is small) or carries an explicit accepted deviation with a rationale written for the verifier to act on
The three measurement artifacts stay directly comparable because the 02-01 harness is byte-for-byte unmodified across this re-run (D-10's instrument stays frozen)
.planning/phases/02-ui-performance/02-PERF-REMEASURE.json — third measurement of all 15 surfaces against archi-dev-box under recorded current conditions
.planning/phases/02-ui-performance/02-FINDINGS.md — a `## Re-measurement (gap closure)` section with the three-way table, per-surface verdicts, and the accepted-deviation list
02-PERF-REMEASURE.json -> 02-PERF-BASELINE.json / 02-PERF-AFTER.json — joined by surface `id`, compared on `revisitMs`, `revisitRpcCount` and per-run `samples` spread
Each verdict in 02-FINDINGS.md -> the specific artifact field that supports it — the citation discipline 02-01 established for this doc
Close verification gap 2: six surfaces measured unimproved or regressed on revisit time in 02-PERF-AFTER.json — Discover (1083 -> 1257ms), Server (738 -> 849ms), Web5 (566 -> 709ms), AppDetails (1204 -> 1510ms), OpenWrtGateway (663.5 -> 1148ms) and wallet/send-flow (2607 -> 2556ms, essentially unchanged) — contradicting 02-08's own must-have. 02-FINDINGS.md flagged an environmental-noise theory for exactly these rows (baseline taken 10:30 local, the after-run at 01:27 the next day on the same multi-service node that was at 85% disk and doubles as the build server) but explicitly did not resolve it. Conditions have since changed materially: roughly 118G was freed on this box, so the disk-pressure component of that theory is no longer present in the same form.

Purpose: PERF-02 and PERF-03 are both marked partial in 02-VERIFICATION.md solely because these rows have no verdict. A measurement with an untested confound is not evidence either way — this plan produces the third data point that turns "regressed or noisy, unresolved" into a per-surface answer. D-10's discipline is unchanged: name the cause from measurement before touching code.

Output: a third harness run (02-PERF-REMEASURE.json) taken with the instrument unmodified and the run conditions recorded, a three-way comparison that reports dispersion rather than bare medians, and a written verdict per named surface — cleared, fixed, or explicitly accepted with rationale.

Coverage: this plan covers verification gap 2 and both human-verification item 2 and the timing half of PERF-02/PERF-03. Gap 1 (Server's remount) is covered by 02-09-PLAN.md; the full multi-source coverage audit for this gap-closure set lives in 02-09-PLAN.md's <source_audit> section.

Constraints: the 02-01 harness (neode-ui/e2e/perf/{surfaces,measure,surface-perf.spec}.ts) is frozen — changing it would break comparability with both committed artifacts and invalidate the whole exercise. Deploy only to archi-dev-box, frontend-only, and only if a fix actually lands (D-15 — no fleet, no OTA). No new features. No visual or animation changes.

<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 @CLAUDE.md

@.planning/phases/02-ui-performance/02-CONTEXT.md @.planning/phases/02-ui-performance/02-VERIFICATION.md

Read only these parts of the large phase artifacts:

  • .planning/phases/02-ui-performance/02-FINDINGS.md## Method (the exact run command, sample semantics and field meanings), the ## Results table, and ## Outstanding (the timing-variance caveat is the last-but-one bullet). Skip the ## Addendum, which is about a different, already-closed issue.
  • .planning/phases/02-ui-performance/02-09-SUMMARY.md — what changed in the build this run measures.
  • neode-ui/e2e/perf/surfaces.ts — surface ids, navSteps and the transit-confound notes for openwrt-gateway, marketplace and marketplace-app-details.

Do not read 02-PERF-BASELINE.json, 02-PERF-AFTER.json or 02-PERF-REMEASURE.json into context — they are 50-60KB each and reading all three would consume most of this plan's budget for no benefit. Extract the fields you need with node -e or jq and work from the extracted table.

Task 1: Re-run the frozen harness against archi-dev-box under recorded conditions .planning/phases/02-ui-performance/02-PERF-REMEASURE.json `ARCHY_PASSWORD` is exported in the environment, `curl -sS -o /dev/null -w '%{http_code}' http://archi-dev-box/` returns a 2xx/3xx, and the bundle served from `/opt/archipelago/web-ui` is the one 02-09 deployed. If the password is unset, stop and report it — do not substitute a local dev server, the mock backend or the `:8100` preview, none of which can answer a question about real-node timing. `.planning/phases/02-ui-performance/02-FINDINGS.md` `## Method` — the exact command, the `runs`/median/`samples` semantics, and the `firstVisitMs`/`revisitMs`/`revisitRpcCount`/`maxConcurrentRpc`/`remounted` field definitions this run must reproduce identically. `neode-ui/e2e/perf/surface-perf.spec.ts` and `measure.ts` — read to confirm the env-var contract (`ARCHY_BASE_URL`, `ARCHY_PERF_OUT`, `ARCHY_PERF_RUNS`), not to change them. Produce the third measurement artifact with the instrument untouched.

Before running, confirm the harness is unmodified since the after-run: git diff --stat 3ee20430 -- neode-ui/e2e/perf/ must be empty. If it is not, stop — a modified instrument makes the three artifacts incomparable and this entire plan pointless. (keepalive-remount-probe.spec.ts, added by 02-09, is a separate spec and does not count as a modification of the three harness files; confirm the diff is limited to that new file if anything shows.)

Record the run conditions BEFORE and AFTER the run, into the SUMMARY and into Task 2's findings section — these are the variables the noise theory turns on, and 02-08 recorded none of them: wall-clock local time at start and end, df -h / (disk usage — roughly 118G was freed since the after-run, so this number should differ materially from the 85% recorded then), uptime load averages, and whether any build, test run or container churn was active on the box during the window. The point is that this run's conditions are stated rather than inferred later.

Prefer a start time close to the baseline's 10:30 local window if the schedule allows — that is the controlled comparison 02-VERIFICATION.md's human-verification item 2 asks for. If the run must happen at a different hour, do not skip it and do not pretend the hour matches: record the actual time and compensate for the uncontrolled variable by sampling more, per the next paragraph.

Run with ARCHY_PERF_RUNS=5 rather than the default 3. This changes no harness code — it is the harness's own env knob — and the reported revisitMs/firstVisitMs stay medians, so they remain directly comparable to both prior artifacts. The extra samples exist so Task 2 can compute a real spread per surface instead of comparing three bare medians, which is the only way to separate a genuine regression from run-to-run variance.

Command shape (password supplied from the environment, never inline in a committed file or a transcript): cd neode-ui && ARCHY_BASE_URL=http://archi-dev-box ARCHY_PERF_RUNS=5 ARCHY_PERF_OUT=../.planning/phases/02-ui-performance/02-PERF-REMEASURE.json npx playwright test e2e/perf/surface-perf.spec.ts --project=chromium --reporter=line, with ARCHY_PASSWORD already exported.

Expect 15 rows. Mesh and Chat may again come back unmeasured — record whichever reason applies rather than presenting them as anything else, exactly as both prior runs did. Note one changed condition worth checking: the after-run's Chat block was the disk-usage toast from HealthNotifications.vue intercepting the close button, which was a symptom of the 85% disk; with the space freed, that block may simply be gone. If Chat measures this time, record it as a bonus data point and say plainly that it has no baseline-comparable counterpart.

If a surface errors or the run aborts, re-run once and keep both transcripts; do not hand-edit the artifact. Commit and push the artifact plus the recorded conditions (git add by path, Co-Authored-By trailer, git push gitea-ai main). test -f .planning/phases/02-ui-performance/02-PERF-REMEASURE.json && node -e "const r=require('./.planning/phases/02-ui-performance/02-PERF-REMEASURE.json'); const rows=r.surfaces||r.results||r; console.log('rows:', Array.isArray(rows)?rows.length:Object.keys(rows).length); console.log('baseUrl:', r.baseUrl, 'runs:', r.runs, 'commit:', r.commit)" git diff --stat 3ee20430 -- neode-ui/e2e/perf/surfaces.ts neode-ui/e2e/perf/measure.ts neode-ui/e2e/perf/surface-perf.spec.ts (must print nothing — the instrument is frozen) 02-PERF-REMEASURE.json exists with 15 rows, a baseUrl of http://archi-dev-box and runs: 5; the three harness files are provably unmodified since the after-run commit; the run's clock time, disk usage, load and concurrent-activity state are recorded; the artifact is committed and pushed.

Task 2: Three-way comparison, a verdict per named surface, and the resulting action .planning/phases/02-ui-performance/02-FINDINGS.md Turn three artifacts into one answer per surface.

Build the comparison table with a script, not by reading JSON into context. Extract, for every surface id present in all three artifacts, from each of 02-PERF-BASELINE.json, 02-PERF-AFTER.json and 02-PERF-REMEASURE.json: firstVisitMs, revisitMs, revisitRpcCount, remounted, and — this is the part 02-08 never used — the per-run values inside samples, reduced to min/median/max. The baseline and after artifacts already contain their own samples arrays (02-FINDINGS.md ## Method: "samples holds every individual run so no data is discarded"), so dispersion is computable for all three runs from what is already committed, with no re-run of history required. A one-off node -e script that prints a markdown table is the right tool; keep it in the transcript rather than committing a script file.

Then decide each of the six surfaces 02-VERIFICATION.md named — Discover, Server, Web5, AppDetails, OpenWrtGateway, wallet/send-flow — against this rule, and state which branch the data put it in:

  • Cleared as environmental noise when the baseline, after and re-measure sample ranges overlap materially, or when the re-measure returns to or below the baseline median. Cite the actual ranges. "Overlapping spread" is a claim the numbers must show, not an assertion.
  • Confirmed regression when the re-measure reproduces the after-run's elevated revisit time outside the baseline's sample spread. Then name the cause: identify the responsible mechanism the way 02-08 root-caused the Cloud connection-pool starvation — direct instrumentation on the device, git log bisection against the pre-phase-2 baseline commit a75b6709, and the surface's own RPC/remount fields — not by inspecting code and guessing. State plainly whether the cause is a phase-2 change or pre-existing.

Apply the confounds already documented rather than re-deriving them: OpenWrtGateway's and Discover's RPC counts bleed through from the tab their navSteps transit (Server and Apps respectively), so treat their RPC columns as unreliable and reason from revisitMs and remounted; wallet/send-flow is a modal that remounts by design via BaseModal's v-if, was never in any plan's files_modified, and its anomaly (revisit slower than first visit, zero RPC either way) is pure client-side cost — if it is confirmed as a real, still-present cost, name the mechanism.

Act on each verdict, in bounds:

  • Cleared -> no code change. Write the verdict and the data.
  • Confirmed AND caused by a phase-2 change AND fixable small -> fix it. "Small" means: confined to one file, covered by a vitest assertion that fails without it, and followed by npm test + npm run type-check + npm run build green, an ARCHIPELAGO_TARGET=archipelago@archi-dev-box scripts/deploy-to-target.sh --frontend-only deploy (D-15, archi-dev-box only), and a targeted re-run of the harness for the affected surface(s) proving the number moved. No visual or animation change is permitted as part of any such fix.
  • Confirmed but caused by something pre-existing, or fixable only by a change that fails any of those bounds -> record an explicit accepted deviation. Write it for the verifier to act on: which surface, the measured numbers across all three runs, the named cause, why it is out of this phase's bounds, and where it belongs (a specific requirement such as UIFIX-06, or a named later phase). An accepted deviation with a named cause and a destination is a resolution; a shrug is not.

Write all of it into a ## Re-measurement (gap closure) section in 02-FINDINGS.md: the run header and recorded conditions from Task 1, the three-way table with dispersion, a verdict line per named surface, and a short ### Accepted deviations subsection listing anything not fixed. Keep the doc's citation discipline — every verdict points at the field that supports it. Keep the doc's candor: if the re-measure shows the after-run was mostly environmental and the architecture is fine, say so and show it; if it shows real regressions the phase shipped, say that just as plainly.

Finally, reconcile the record: if the verdicts change what PERF-02/PERF-03 can honestly claim, update the status note for those rows in .planning/REQUIREMENTS.md's coverage table to point at this section, so the next verification pass reads the resolved state rather than the old partial one. Commit and push (Co-Authored-By trailer, git push gitea-ai main). grep -q "## Re-measurement (gap closure)" .planning/phases/02-ui-performance/02-FINDINGS.md && for s in Discover Server Web5 AppDetails OpenWrtGateway "send flow"; do grep -q "$s" .planning/phases/02-ui-performance/02-FINDINGS.md && echo "present: $s"; done cd neode-ui && npm test 2>&1 | tail -10 && npm run type-check (required whether or not a fix landed — the suite must stay green) 02-FINDINGS.md has a ## Re-measurement (gap closure) section containing the recorded run conditions, a three-way table with per-run spread, and one verdict per named surface — each either cleared with the overlapping-range data that clears it, fixed with the change and its proof, or listed under ### Accepted deviations with a named cause and a destination. The vitest suite and type-check are green. Any fix that landed was deployed to archi-dev-box only and re-measured. Work is committed and pushed.

<threat_model>

Trust Boundaries

Boundary Description
operator workstation -> archi-dev-box UI login The real node password crosses this boundary at runtime to drive the harness
harness -> committed measurement artifact Observed traffic metadata is written into a repository file
build artifact -> /opt/archipelago/web-ui on a real node Only if a confirmed-regression fix lands

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-02-10-01 Information Disclosure harness login (ARCHY_PASSWORD) high mitigate Password read only from the environment at runtime; never inlined into a command that gets pasted into a committed file, never written to 02-PERF-REMEASURE.json, the findings doc or the SUMMARY. The same handling 02-01 and 02-08 used.
T-02-10-02 Information Disclosure 02-PERF-REMEASURE.json contents medium mitigate The harness records RPC method names and timings only, never request or response bodies — its original threat mitigation, preserved because the harness is unmodified. Spot-check the artifact for anything resembling a token, credential or personal path before committing.
T-02-10-03 Tampering frozen harness files medium mitigate Task 1 gates on git diff --stat over the three harness files being empty; a modified instrument silently invalidates every comparison in this plan, so it is checked rather than assumed.
T-02-10-04 Tampering scripts/deploy-to-target.sh --frontend-only (conditional) medium mitigate Only triggered by a confirmed, phase-2-caused, small fix; frontend-only, archi-dev-box only, no OTA and no fleet/catalog path (D-15); served bundle grepped post-deploy per CLAUDE.md's silent-no-op-build gotcha.
T-02-10-05 Repudiation measurement provenance low mitigate The artifact header carries baseUrl, commit and runs, and Task 1 additionally records clock time, disk usage and load — so a later reader can tell which build and which machine state produced each number instead of trusting a verdict's summary of them.
T-02-10-SC Tampering npm/pip/cargo installs high accept No package-manager install is planned — re-running an existing harness and comparing committed JSON needs no new dependency. If one becomes necessary, halt and route through the Package Legitimacy Gate before installing anything.
</threat_model>
- `02-PERF-REMEASURE.json` exists, 15 rows, `baseUrl: http://archi-dev-box`, `runs: 5`, produced by a provably unmodified harness. - Every one of the six surfaces named in 02-VERIFICATION.md gap 2 has a verdict in `02-FINDINGS.md` backed by cited artifact fields including per-run spread. - The environmental-noise theory is either supported or refuted by stated data; the run conditions for the new run are recorded and the prior two runs' known conditions are restated for comparison. - Anything not fixed appears under `### Accepted deviations` with a named cause and a destination requirement or phase. - Full vitest suite and type-check green; any fix deployed to archi-dev-box only (D-15). If a fix landed as part of Task 2, the D-11 pass bar applies to the affected surface: on archi-dev-box, revisit that surface after having visited it once in the session and confirm content paints immediately with no spinner or blank frame, and that nothing about its layout, margins or transition animation changed.

<success_criteria>

  • Verification gap 2 is closed: no surface is left in the unexplained "regressed, cause unknown, confound untested" state that blocked the phase's clean pass.
  • The noise-versus-regression question is answered with dispersion data from three runs, not asserted from three medians.
  • Real regressions traceable to phase 2 and cheap to fix are fixed and re-measured; everything else is an explicit, actionable accepted deviation.
  • The instrument stayed frozen, so all three artifacts remain a comparable series for any future run. </success_criteria>
Create `.planning/phases/02-ui-performance/02-10-SUMMARY.md` when done. Record the run conditions verbatim, the three-way table, each surface's verdict and the branch it fell into, any fix with its before/after numbers, the full accepted-deviation list, and whether Mesh/Chat measured this time.