Archipelago — open-source initial import

This commit is contained in:
Archipelago
2026-08-12 10:55:50 +00:00
commit e9c69062fe
1936 changed files with 443175 additions and 0 deletions
@@ -0,0 +1,343 @@
---
phase: 02-ui-performance
plan: 08
type: execute
wave: 5
depends_on: ["02-03", "02-05", "02-06", "02-07"]
files_modified:
- .planning/phases/02-ui-performance/02-PERF-AFTER.json
- .planning/phases/02-ui-performance/02-FINDINGS.md
- neode-ui/src/views/dashboard/keepAliveRoutes.ts
autonomous: false
requirements: [PERF-01, PERF-02, PERF-03]
must_haves:
truths:
- "The same harness that produced the baseline is re-run against archi-dev-box and produces a directly comparable after-artifact"
- "For every surface the findings doc named as slow, the after-artifact shows a lower revisit time and a lower revisit RPC count than the baseline"
- "For every main tab registered for instance caching, the after-artifact's remount probe shows the component instance survived a tab round-trip"
- "Revisiting any main tab already visited this session on archi-dev-box shows no spinner and no blank screen (D-11 pass bar)"
- "Reopening any secondary screen already opened this session on archi-dev-box shows no blocking reload (D-11 pass bar)"
- "The instance-cache cap is set from observed on-device memory rather than from an estimate, and the observation is recorded"
- "The build shipped to the dev pair actually contains this phase's changes, confirmed by grepping the built bundle"
- "The deploy reached the dev pair only — no fleet node and no OTA channel received this build (D-15)"
- "A surface that regressed against its baseline is recorded as a regression rather than averaged away"
- statement: "Extended use across many tab visits on archi-dev-box does not reintroduce sluggishness — memory and idle CPU stay flat"
verification: backstop
prohibitions:
- "MUST NOT present inferred, code-read, or cherry-picked numbers as measured profiling results, and MUST NOT omit a surface from the results because it was hard to measure — an unmeasured surface is recorded as unmeasured, never as improved"
- "MUST NOT achieve perceived speed by removing behavior or hiding state — no suppressing the refresh indicator, no dropping a fetch a surface needs, no disabling a feature to win the metric"
- "MUST NOT push this phase's build beyond the dev pair — no fleet node, no OTA channel, no alpha-tester deploy path"
artifacts:
- path: ".planning/phases/02-ui-performance/02-PERF-AFTER.json"
provides: "Post-fix measurements from the same harness and the same target as the baseline"
- path: ".planning/phases/02-ui-performance/02-FINDINGS.md"
provides: "A Results section comparing baseline to after, per surface, including any regression"
key_links:
- from: ".planning/phases/02-ui-performance/02-PERF-AFTER.json"
to: "neode-ui/e2e/perf/surface-perf.spec.ts"
via: "produced by re-running the plan 02-01 harness unmodified against the same target"
pattern: "surface-perf"
- from: ".planning/phases/02-ui-performance/02-FINDINGS.md"
to: ".planning/phases/02-ui-performance/02-PERF-BASELINE.json"
via: "the Results section pairs each after row with its baseline row"
pattern: "02-PERF-BASELINE"
---
<objective>
Deploy this phase's frontend to the dev pair, re-measure every surface on archi-dev-box
with the same harness that produced the baseline, and walk the D-11 pass bar by hand.
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.
</objective>
<execution_context>
@$HOME/.claude/gsd-core/workflows/execute-plan.md
@$HOME/.claude/gsd-core/templates/summary.md
</execution_context>
<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
</context>
<tasks>
<task type="auto">
<name>Task 1: Build and deploy the frontend to the dev pair only</name>
<precondition>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</precondition>
<files>neode-ui/src/views/dashboard/keepAliveRoutes.ts</files>
<read_first>
- `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.
</read_first>
<action>
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.
</action>
<verify>
<automated>cd neode-ui && npm run type-check && npm run test && npm run build && for s in shouldKeepAlive RefreshIndicator app-catalog; do grep -rqs "$s" ../web/dist/neode-ui/ || { echo "MISSING FROM BUNDLE: $s"; exit 1; }; done; echo BUNDLE_OK</automated>
</verify>
<acceptance_criteria>
- `npm run type-check`, `npm run test` and `npm run build` all exit 0
- `web/dist/neode-ui/` contains `shouldKeepAlive`, `RefreshIndicator` and at least one cache key introduced by this phase
- The deploy command actually run is recorded verbatim in the SUMMARY, along with every host it touched
- No alpha-tester or fleet host appears in that host list; no OTA or release path was invoked
- `KEEP_ALIVE_MAX`'s value at the end of this task is recorded together with the on-device memory reading that justifies it
- Every change is committed and pushed, staged by explicit path
</acceptance_criteria>
<done>A verified-real build is running on the dev pair and nowhere else, and the instance-cache cap is set from an observed memory reading rather than an estimate.</done>
</task>
<task type="auto">
<name>Task 2: Re-measure on archi-dev-box and write the before/after comparison</name>
<precondition>The dev-pair deploy from Task 1 is live — the archi-dev-box UI serves the new bundle (grep the served asset for `shouldKeepAlive`, not just the local `web/dist` copy)</precondition>
<files>.planning/phases/02-ui-performance/02-PERF-AFTER.json, .planning/phases/02-ui-performance/02-FINDINGS.md</files>
<read_first>
- `neode-ui/e2e/perf/surface-perf.spec.ts`, `neode-ui/e2e/perf/measure.ts`, `neode-ui/e2e/perf/surfaces.ts` — the harness from plan 02-01 and its `ARCHY_BASE_URL` / `ARCHY_PERF_OUT` contract
- `.planning/phases/02-ui-performance/02-PERF-BASELINE.json` — the run header records the exact target and sample count the after-run must match
- `.planning/phases/02-ui-performance/02-FINDINGS.md` — the per-surface table and ranked fix order this task extends with results
- `.planning/phases/02-ui-performance/02-04-SUMMARY.md` — which main-tab paths ended up registered for instance caching, so the remount-probe expectation is known per surface
- `.planning/phases/02-ui-performance/02-03-SUMMARY.md` — which secondary screens were converted and which were reported as gaps
</read_first>
<action>
Re-run the plan 02-01 harness unmodified against archi-dev-box, with the same
`ARCHY_BASE_URL` and the same sample count recorded in the baseline's run header, and
`ARCHY_PERF_OUT` pointed at
`.planning/phases/02-ui-performance/02-PERF-AFTER.json`. Do not edit the harness to
make numbers look better; if a selector genuinely broke because a view's markup
changed, fix the selector, re-run BOTH the baseline target and the after target so the
pair stays comparable, and say so in the run header.
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.
</action>
<verify>
<automated>node -e "const p='/home/archipelago/Projects/archy/.planning/phases/02-ui-performance/'; const a=require(p+'02-PERF-AFTER.json'), b=require(p+'02-PERF-BASELINE.json'); const ar=a.results??a, br=b.results??b; if(!Array.isArray(ar)||ar.length!==br.length){console.error('row count mismatch',ar.length,br.length);process.exit(1)} console.log('rows',ar.length)" && grep -qF '## Results' /home/archipelago/Projects/archy/.planning/phases/02-ui-performance/02-FINDINGS.md && grep -qF '## Outstanding' /home/archipelago/Projects/archy/.planning/phases/02-ui-performance/02-FINDINGS.md && echo OK</automated>
</verify>
<acceptance_criteria>
- `.planning/phases/02-ui-performance/02-PERF-AFTER.json` exists, parses, and has the same row count as the baseline
- Its run header records the same `baseUrl` and `runs` as the baseline run header, or explains any difference
- `02-FINDINGS.md` contains `## Results` with one row per surface and `## Outstanding`
- Every Results row's Verdict is one of `improved`, `unchanged`, `regressed`, `unmeasured`
- Every surface the findings originally named as slow has a numeric after value, or a recorded reason it is `unmeasured`
- Every main-tab path registered in `KEEP_ALIVE_PATHS` has `After remounted` false
- `## Outstanding` lists every regressed surface, every unmeasured surface, every gap reported by plan 02-03, and every unsettled flagged assumption
- Both artifacts are committed
</acceptance_criteria>
<done>The same instrument on the same target says, in committed numbers, what this phase actually changed per surface — including anything that got worse or could not be measured.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 3: D-11 pass bar on archi-dev-box — is the sluggishness gone on-device</name>
<what-built>
The whole phase, running on archi-dev-box: main tabs instance-cached with capped
eviction and background revalidation, secondary screens cached per item, the Mesh
graph and map held for the session, Server and Home fetches cached with the wallet
always re-checking on re-entry, and the AIUI panel persisting across tab switches with
its two D-14 defaults. Automated before/after numbers are already committed in
`02-FINDINGS.md` `## Results`.
</what-built>
<how-to-verify>
This is the D-11 pass bar. The bar is: no visible spinner and no blank screen when
revisiting a tab or secondary screen already visited this session. First visits may
still show loading — that is allowed.
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?
</how-to-verify>
<resume-signal>Type "approved" if the pass bar is met, or describe what you saw: which step, which surface, what happened instead.</resume-signal>
</task>
</tasks>
<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 `## Results` and `## Outstanding`
- `KEEP_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 `## Outstanding` that 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 `## Outstanding` as 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 `## Outstanding` with its measured cause.
</assumptions_and_flagged_items>
<verification>
- `cd neode-ui && npm run type-check && npm run test && npm run build` all exit 0
- The built bundle contains this phase's new symbols
- The deploy touched dev-pair hosts only, recorded verbatim in the SUMMARY
- `02-PERF-AFTER.json` has the same row count as `02-PERF-BASELINE.json`
- `02-FINDINGS.md` contains `## Results` and `## Outstanding`
- The D-11 pass-bar checkpoint is approved on archi-dev-box
</verification>
<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>
<output>
Create `.planning/phases/02-ui-performance/02-08-SUMMARY.md` when done. It MUST record:
the exact deploy command and every host it touched; the on-device memory reading and the
final `KEEP_ALIVE_MAX`; the per-surface before/after verdicts; the full `## Outstanding`
list; and the human verdict on the D-11 pass bar in the user's own words.
</output>