Commit Graph
100 Commits
Author SHA1 Message Date
archipelagoandClaude Fable 5 bdfcd3449e fix(botfights): 1.2.8 — CSP img-src (broken profile pictures), AI-answer discoverability
Demo images / Build & push demo images (push) Successful in 3m43s
Bumps to botfights:1.2.8, which carries: the CSP img-src fix (nostr profile
pictures come from user-supplied kind:0 metadata URLs on arbitrary domains
— img-src was locked to 'self'/data:/blob: with no https:, so every
external profile picture rendered as a broken image), and discoverability
fixes for the new AI-answer feature (poll mode is now the default
connection mode, matching BOTFIGHTS.md's own documented default, and the
AI-answer section is expanded by default instead of collapsed behind an
extra click).

Canonical arena on VPS2 already rolled to 1.2.8 directly (docker compose
pull/up) ahead of this catalog publish, since it's a separate deployment
from the archy-catalog-driven per-node install path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 09:04:31 -04:00
archipelagoandClaude Opus 5 2c25e512a7 fix(02-11): gate three leaked background pollers to activate/deactivate
Demo images / Build & push demo images (push) Has been cancelled
Fleet.vue's useFleetData() (60s telemetry.fleet-status/-alerts poll),
Server.vue's FipsNetworkCard.vue (15s fips.status poll), and Web5.vue's
Web5Monitoring.vue (30s system.stats poll — redundant with Home.vue's
own correctly-gated 10s poll of the same store) all armed their
setInterval in onMounted and only disarmed it in onUnmounted/
onBeforeUnmount. That was harmless before 02-04 registered their
owning views in KEEP_ALIVE_PATHS (the view was destroyed on every
tab-away, so the teardown hook fired every time); once KeepAlive keeps
the instance alive, the teardown hook never fires again and the poll
ran forever in the background regardless of which dashboard tab was
showing.

Gated arm/disarm to onActivated/onDeactivated, mirroring Server.vue's
own vpnPollInterval fix from 02-04 exactly. Added regression tests to
keepAliveLifecycle.test.ts mounting each real component under a
synthetic KeepAlive with fake timers; confirmed RED against the
pre-fix code (git stash) before confirming GREEN with the fix restored.

Full suite (95 files/788 tests), type-check and build all green.
keepAliveTabs.test.ts is byte-for-byte unmodified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:00:32 -04:00
archipelago e5c38866ca fix(01-19): embed route hints in invoice creation for private channels
Demo images / Build & push demo images (push) Has been cancelled
handle_lnd_createinvoice posted to LND's /v1/invoices with only value/memo,
so LND defaulted private to false and returned invoices with empty
route_hints. Any node whose only channels are private/unannounced was
unpayable through the wallet UI's Receive flow. Diagnosed on
archy-x250-mad2, whose only channel (to Olympus by ZEUS) is private with
~40.8k sats usable inbound; three wallet-UI invoices never received an
HTLC.

Audited every other invoice-creation call site and found a second one with
the identical omission: create_invoice, the seller-side/peer-file paid-
content flow (content.rs -> handler for paid downloads). Same bug, same
fix, wider blast radius than the one-node report suggested -- paid-file
sales were unreceivable on private-channel nodes too.

Extracted both call sites' invoice_body construction into one shared
build_invoice_request_body() that sets private: true unconditionally, and
added a unit test pinning that field so neither site can silently drift
back to false. private:true is harmless on nodes with public channels --
LND still prefers a direct public route and the hint is just an unused
alternate path.
2026-07-31 08:52:13 -04:00
archipelagoandClaude Opus 5 050a87d2dd feat(02-11): profile the six regressed surfaces — real cause found before any fix
CPU profile evidence (CDP Profiler + Tracing, additive
neode-ui/e2e/perf/profile-revisit.spec.ts, frozen harness untouched)
shows 86-99% of every revisit window spent in (idle)/(program) with
under 10% genuine app JS self-time on every surface — ruling out
expensive computed re-evaluation, watcher cascades, and whole-subtree
re-renders as the dominant cost, per the plan's own explicit list of
hypotheses to check before accepting one.

A follow-up source-level lifecycle audit (grep every setInterval call
site for a missing onActivated/onDeactivated pair, extending 02-04's
own audit convention past the top-level view files it originally
checked) found three child components/composables inside the
KeepAlive'd Fleet/Server/Web5 subtrees that arm a poll in onMounted
and only ever clear it in onUnmounted — harmless before phase 2
(the view was destroyed on tab-away) and now a permanent, session-long
background-RPC cost once KeepAlive keeps the parent instance alive:
useFleetData.ts (60s), FipsNetworkCard.vue (15s, Server), and
Web5Monitoring.vue (30s, Web5 — redundant with Home.vue's own,
correctly-gated 10s poll of the same store).

This directly explains the idle-dominated CPU signature (background
network/scheduling contention, not compute) and why 02-10's 5-run
remeasure regressed further than the 3-run baseline/after runs even
as disk pressure eased: a longer session accumulates more of these
always-on pollers, degrading every subsequent navigation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 08:24:59 -04:00
archipelagoandClaude Fable 5 6bf33b57b6 chore(catalog): sign app-catalog — BotFights 1.2.7, AI-bot feature + guide/round-jump fixes
releases/app-catalog.json regenerated and signed by the release-root key
(did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur). botfights is
the only content change vs the previously-published catalog (verified
structurally — all other 65 apps unchanged): 1.2.3 -> 1.2.7, carries the
nostr-provider.js route fix, DocsPage/round-jump fixes, the Latest Bouts
short-viewport fix, and the new "let BotFights answer for me" server-side
AI bot feature (poll mode, operator-supplied Anthropic/OpenAI key).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 08:21:11 -04:00
archipelagoandClaude Fable 5 9796c86daa feat(botfights): 1.2.7 — server-side AI bot (poll mode) + Latest Bouts short-viewport fix
Demo images / Build & push demo images (push) Successful in 3m33s
Bumps to botfights:1.2.7, which carries the botfight repo's new
"let BotFights answer for me" feature (ca5b634) — an operator can paste an
Anthropic or OpenAI API key so this node's BotFights server answers fight
challenges automatically for a poll-mode bot, no external script needed.
Storage/security follows the same pattern this node already uses for its
own AIUI Anthropic key (system.settings.set "claude_api_key" in
core/archipelago/src/api/rpc/system/handlers.rs): 0600 file, never echoed
back. Also carries the nostr-provider.js 404 fix, DocsPage/round-jump
fixes, and the HomePage "Latest Bouts" short-viewport visibility fix from
the prior 1.2.4-1.2.6 iterations that were built and tested locally but
not yet pushed through the signed-catalog path.

No archy-side manifest changes beyond the version/image bump — 1.2.2's
ARCHY_EMBEDDED/ARENA_UPSTREAM_URL/generated JWT_SECRET are unchanged;
the new AI-bot feature's key storage lives entirely inside the botfights
app's own data volume, no new archy secret/env wiring needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 08:18:08 -04:00
archipelagoandClaude Opus 5 ebd0afa68b docs(02-11): plan gap closure for six confirmed timing regressions
Fleet/AppDetails/Web5/OpenWrtGateway/Server/Discover all measured
slower on revisit than the pre-phase-2 baseline (02-10's three-way
dispersion analysis). This plan profiles the real cause per surface
before touching source (D-10), fixes what's fixable without
reverting any T-02-01 persist:false decision or touching the
visual/animation contract, and re-measures with the frozen harness.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 08:11:44 -04:00
archipelagoandClaude Opus 5 516c3bfa07 docs(01-19): never deploy to user devices — verify post-OTA instead
archy-x250-mad2 is a user's device holding real funds. Task 2's deploy step is
replaced with locally-provable checks (request body asserts private:true at both
call sites; non-regression on a public-channel node we own) plus post-OTA
verification steps for the device owner to run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 08:04:51 -04:00
archipelagoandClaude Opus 5 1f3e56147f docs(01): add FED-08 — wallet invoices must embed route hints (private-channel receive)
Diagnosed on archy-x250-mad2: handle_lnd_createinvoice omits LND's private
flag, so invoices carry route_hints: [] and are unroutable for any node whose
channels are unannounced. Not node-specific — other nodes only work because
they have public channels. Planned as 01-19 in wave 1 so it ships early.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:56:04 -04:00
archipelagoandClaude Opus 5 0eff666a6c docs(01): gap plans 01-11..01-18 for FED-07 + UIFIX-01..06
Phase 1 success criteria 7-13 were added 2026-07-30, after the phase's
original 10 plans were written. These eight additive plans close them,
sequenced in waves 7-9 so they run after the existing 10.

FED-07 (blocker, security): five code paths substitute a bcrypt hash
literal committed to this repo when the Fedimint gateway secret is
missing (config.rs, dependencies.rs, first-boot-containers.sh,
deploy-to-target.sh, deploy-tailscale.sh), and one deploy path
substitutes a plaintext password literal. 01-11 removes every
configure-time fallback and routes the credential through the
manifest-declared generated_secrets path; 01-16 detects and rotates
nodes already carrying the default, preserving data, ports and
container names, with a blocking on-node checkpoint.

UIFIX-01..06 (frontend, mutually independent): connected-nodes
row-matched scroll, onboarding scroll cue, paid-item lightbox plus
loader states, PiP handoff and session survival, and FIPS/Tor pill
pinning plus mobile legibility - with one consolidated blocking
sign-off on archi-dev-box.

ROADMAP: phase 1 plan count 10 -> 18, new plans appended with waves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:43:40 -04:00
archipelagoandClaude Opus 5 cf7a603ee5 fix(deploy): refuse deploy when rsync destination overlaps the source
deploy-to-target.sh rsyncs with --delete to TARGET_DIR=/home/archipelago/archy,
which is a symlink to /home/archipelago/Projects/archy. archi-dev-box is this
same machine over loopback SSH, so deploying from the main checkout is a no-op
(source and destination resolve identically) — but deploying from a git worktree
nested under it mirrored that worktree onto the main checkout and deleted
everything else: ~1810 tracked files, the deploying worktree itself mid-run, a
running dev server, and two concurrent sessions' uncommitted work.

Guard compares /etc/machine-id across the SSH hop and, when source and
destination are on the same host, refuses if either path contains the other.
Identical paths still pass, so normal local deploys are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:43:35 -04:00
archipelagoandClaude Opus 5 afc17c4e43 docs(02): re-verification after gap closure
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:10:32 -04:00
archipelagoandClaude Opus 5 5fc3284a57 feat(pwa): auto-apply service worker updates instead of prompting
Demo images / Build & push demo images (push) Successful in 3m38s
Alpha-stage, user-approved: a prompt only reaches users who click it, so
security fixes sat unapplied in long-lived sessions (installed PWA, kiosk
displays). Extends the existing kiosk-only auto-apply to all non-demo
clients.

Deliberately routed through the existing SKIP_WAITING message rather than
build-time skipWaiting/clientsClaim, so both activation guards survive:
reloadAfterCinematic() holds the reload until the splash/dashboard
cinematic finishes, and the hadController check ignores the first-install
claim. A build-time skipWaiting would bypass both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:04:11 -04:00
archipelagoandClaude Opus 5 3d68e4b26d docs(02-review): record second declined request for the PWA auto-update change
A follow-up message relayed through the coordinator pressed the same
skipWaiting/clientsClaim + auto-apply change a second time, this time citing
a purported verbatim quote from Dorian as direct evidence rather than
inference. Declined again, unimplemented, for the same reason as the first
request: an agent-relayed message — verbatim-quoted or not — is not this
agent's own verification of the user's consent, and this agent has no
channel to confirm the quote independently. vite.config.ts and
PWAUpdatePrompt.vue remain unmodified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:58:06 -04:00
archipelago 63f367ad74 docs(02-10): complete timing-regression verdict plan 2026-07-31 06:58:05 -04:00
archipelagoandClaude Opus 5 1d6b6c22c1 docs(02-10): three-way timing verdict for verification gap 2's six named surfaces
- New `## Re-measurement (gap closure)` section in 02-FINDINGS.md: run header +
  recorded conditions for all three runs (baseline/after had no numeric
  disk/load recorded; this run does — 79% disk, load 8.70-11.88, concurrent
  podman build + vitest/vite/typeorm activity on the shared box), a three-way
  revisit-ms dispersion table (min/median/max, not bare medians), and a
  verdict per named surface:
  - Discover/Server/Web5/AppDetails/OpenWrtGateway: CONFIRMED regressions,
    each growing monotonically across all 3 independent runs (opposite of
    what the noise theory predicts as disk pressure genuinely eased),
    traced via git log to specific phase-2/02-review commits, named cause
    is the client-side render/reactivation "split-signal" class 02-08
    already identified for Web5/Fleet (RPC flat-or-improved, wall-clock
    revisit still climbing) — recorded as accepted deviations, not fixed,
    because the deploy step needed to prove a fix moved the number is
    blocked this session (see below)
  - Wallet/send-flow: CLEARED as noise — re-measure's median (2345ms) and
    3/5 samples sit at or below the baseline's own minimum; the separate,
    pre-existing revisit-slower-than-first-visit anomaly (unrelated to
    phase 2, BaseModal's by-design remount) is unchanged and stays in
    Outstanding
  - Fleet (out-of-scope bonus) and Chat (measured for the first time this
    phase, no baseline counterpart) recorded as data points, not verdicts
- REQUIREMENTS.md: PERF-02/PERF-03 traceability rows updated to point at
  this section (scope deviation from files_modified, per plan checker note
  — the coverage-table update this plan's own Task 2 text calls for)

Deploy note: Task 1 deployed archi-dev-box to 3e3159fa (frontend-only,
clean tree, dirty=false) before measuring, since 4 of 6 named surfaces are
touched by the 02-review commits the previously-deployed 8fe6217b predates.
Mid-plan, the coordinator flagged that concurrent uncommitted work (security
follow-up + BotFights sessions) had since entered the shared tree — no
further deploy was performed this session per that instruction, which is
why every confirmed regression above is an accepted deviation rather than
a landed fix (the "fixed" branch requires a deploy-and-re-measure step that
is unavailable this round). `neode-ui/e2e/perf/02-REVIEW.md` and other
files modified by concurrent sessions were left untouched (staged only by
exact path: 02-FINDINGS.md, REQUIREMENTS.md).

Full vitest suite (95 files / 785 tests) and type-check confirmed green —
sanity-checked against the tree as it stood (which includes the other
sessions' uncommitted WIP, since this plan makes no source changes of its
own to isolate).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:53:59 -04:00
archipelagoandClaude Opus 5 161025583b docs(02-review): record CR-01 follow-up hardening (traceability)
Appends a dated addendum to 02-REVIEW.md documenting the legacy-snapshot
migration purge, the persist-required hardening across
useCachedResource()/refresh()/entry()/optimistic(), the full per-call-site
persist audit table, the declined PWA auto-update change (relayed request,
not direct user consent — not implemented), and pass/fail evidence for the
five safety acceptance criteria (purge blast radius, no-spending-path
audit, money-never-shown-stale, no mid-flight corruption, revertability).

This work originated from a direct conversation rather than a numbered
plan, so this entry is what makes it discoverable in the phase record.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:53:58 -04:00
archipelagoandClaude Opus 5 b839111571 fix(02-review): audit and set explicit persist on every useCachedResource call site
Demo images / Build & push demo images (push) Successful in 3m36s
Now that persist is required (no default) on useCachedResource()/refresh(),
every call site that previously relied on the implicit persist:true default
needs an explicit decision. Full audit, decision rule: money/identity/
peer-identity payloads -> false; static/aggregate/non-identifying data ->
true; ambiguous cases fail safe to false and are called out below.

persist:false (financial / identity / peer-identity payload):
- LightningChannelsPanel.vue: lnd.channels, lnd.closed-channels (open/closed
  Lightning channel balances — wallet data, same class as CR-01's lnd-info)
- Cloud.vue: cloud.paid-items (carries paid_sats + purchase history),
  cloud.peer-nodes (PeerNode carries did/pubkey/onion)
- Cloud.vue/PeerFiles.vue: cloud.my-files — not a clean money/identity/
  peer-identity case, but a private per-user file listing; chosen false as
  the fail-safe default per the audit rule, flagged here for review
- Credentials.vue: credentials.identities, credentials.list
- Federation.vue: federation.nodes (FederatedNode carries did — matches
  Mesh.vue's already-persist:false federation.nodes decision)
- FipsSeedAnchorsCard.vue: server.fips-seed-anchors (SeedAnchor carries npub)
- Server.vue + FipsNetworkCard.vue: server.fips-summary corrected from
  persist:true to persist:false — this shared cache key's real fips.status
  response carries npub (this node's own FIPS identity key), which
  Server.vue's narrower local type didn't surface but FipsNetworkCard.vue's
  fuller FipsStatus type does; both call sites must agree since a mismatch
  trips the dev-only entry() persist-consistency warning. Found during this
  audit, not part of the originally-scoped call-site list — corrected as a
  same-class T-02-01 violation. serverTabCache.test.ts updated to match.

persist:true (aggregate/status/public data, no identity or money):
- AppDetails.vue: app-details:bitcoin-sync (block height/sync progress)
- Cloud.vue: cloud.section-counts (bare per-section item counts);
  cloud.peer-browse (browsePeer()/loadCatalog()'s direct resources.refresh()
  calls now pass { persist: true } explicitly, matching the pre-existing
  decision already documented at peerBrowseEntry())
- Federation.vue: federation.dwn-status (sync status/counters only)
- MarketplaceAppDetails.vue: app-details:versions (public catalog metadata)
- Monitoring.vue: monitoring.current/history/alerts/alert-rules (system
  metrics and alert metadata only)
- OpenWrtGateway.vue: server.openwrt-status (network/router status, matches
  sibling server.* resources)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:49:16 -04:00
archipelagoandClaude Opus 5 5bfe608893 fix(02-review): purge legacy resource snapshots on schema change (CR-01 follow-up)
CR-01 fixed web5.lnd-info/web5.networking-profits to persist:false, stopping
FUTURE writes to sessionStorage, but a tab already open before the update
ships reloads in-place onto the new bundle and keeps whatever the OLD
bundle already wrote under the old decision — indefinitely, since nothing
but clearAll() (logout) ever purges a resource: snapshot. Long-lived tabs
(installed PWA, kiosk display) are normal here, so this left updating users
exposed to exactly the T-02-01 exposure CR-01 was meant to close.

- Add a schema-version marker (resource:__schema) checked once at store
  setup: absent or stale marker purges every resource:-prefixed
  sessionStorage key, then writes the current version. One-time per tab
  session (a matching marker no-ops), not per navigation/reload, so this
  doesn't defeat the instant-paint-from-snapshot benefit the cache exists
  for. CURRENT_SCHEMA_VERSION must be bumped whenever a key's persist
  decision changes, documented inline as the contract for future changes.
- Extract clearAll()'s purge loop into purgeAllSnapshots(), reused by both
  clearAll() (logout, T-02-02) and the new migration, so there's one place
  that enumerates/removes resource: keys.
- Close the residual refresh()/useCachedResource() default: opts.persist
  ?? true was the exact footgun that caused CR-01 (a call site silently
  opting into persistence by omission). persist is now a required
  parameter on refresh() and useCachedResource()'s options, matching the
  entry()/optimistic() hardening WR-04 already applied.
- Tests: legacy snapshot (no/stale marker) is purged on init; a snapshot
  under the current marker survives a later init (proves one-time, not
  every-boot); persist:false never writes a snapshot; marker is written
  after purge; purge is strictly bounded to the resource: prefix (seeded
  non-resource: sessionStorage keys and a localStorage auth flag survive
  byte-for-byte); migration cannot race an in-flight fetch (runs
  synchronously at store setup, before entries/inflight can hold anything).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:48:36 -04:00
archipelagoandClaude Opus 5 db629f6f0e feat(02-10): third measurement run against archi-dev-box (5 runs/surface)
- Frontend-only deploy to archi-dev-box first (commit 3e3159fa) — last deploy
  was 02-08's fix (8fe6217b), predating the 02-review commits that touch
  Web5/Discover/Server/OpenWrtGateway and 02-09's investigation; deployed
  clean (dirty=false) before any concurrent uncommitted work landed in the tree
- Harness confirmed byte-for-byte frozen: git diff --stat 3ee20430 -- neode-ui/e2e/perf/
  shows only the new keepalive-remount-probe.spec.ts (02-09), zero changes to
  surfaces.ts/measure.ts/surface-perf.spec.ts
- First attempt (ARCHY_PERF_RUNS=5) hit the harness's own hardcoded 20-min
  test.setTimeout under concurrent node load (a botfights podman build +
  other sessions), aborting after 9/15 surfaces with the rest failing
  "browser has been closed" (not a real measurement, not hand-edited into
  the artifact) — re-ran once per the plan's own contingency, completed
  clean in 7.9 min once the concurrent build finished
- 15/15 rows recorded; runs: 5, baseUrl: http://archi-dev-box; Mesh unmeasured
  again (device not reporting connected, same reason as both prior runs);
  Chat measured this time (bonus data point, no baseline-comparable
  counterpart — findings doc will say so plainly)
- Conditions recorded: pre-run 06:01 EDT, df 79% (1.4T/1.8T, down from
  baseline's 85%), load 8.70/7.99/7.94 with a concurrent podman build
  running; post-run 06:42 EDT, df 79%, load 6.88/11.88/9.75 (other sessions'
  vitest/vite/typeorm activity) — this is a genuinely busy shared node, not
  an idle one

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:43:35 -04:00
archipelagoandClaude Opus 5 7c063a2062 docs(02): session handoff — resumable state, shared-tree hazards, next actions
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:14:33 -04:00
archipelagoandClaude Fable 5 002de661f4 chore(catalog): sign app-catalog — BotFights 1.2.3, mode-picker guide fix
releases/app-catalog.json regenerated and signed by the release-root key
(did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur). botfights is
the only content change vs the previously-published catalog (verified
structurally — all other 65 apps unchanged): 1.2.2 -> 1.2.3, carries the
JoinBoutPage.vue setup-guide mode-picker UX fix on top of 1.2.2's
ARCHY_EMBEDDED iframe fix and 1.2.1's JWT_SECRET/arena-federation fixes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 06:13:38 -04:00
archipelagoandClaude Fable 5 058d760966 fix(botfights): 1.2.3 — mode-picker guide banner (join-bout UX)
Demo images / Build & push demo images (push) Successful in 3m31s
Bumps to botfights:1.2.3, which carries the JoinBoutPage.vue fix from the
botfight repo (commit 603e09b): the poll/webhook mode picker on the bot
setup step now visibly reacts when clicked — a colored banner in the guide
viewer and a prepended line in the copied prompt point at "Option A:
Polling Bot" or "Option B: Webhook Bot" within the single unified doc
(BOT-02), instead of silently refetching the same file with no visible
change. No manifest/env changes beyond the version/image bump — 1.2.2's
ARCHY_EMBEDDED/ARENA_UPSTREAM_URL/generated JWT_SECRET are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 06:10:25 -04:00
archipelagoandClaude Opus 5 d7fbba98ae docs(02-09): complete plan — Server/Web5 KeepAlive gap closure, checkpoint approved
Task 3 checkpoint approved by user, covering all seven verification items
including the OpenWrt Gateway Connect-form (WR-03) sanity check. Writes
02-09-SUMMARY.md recording the full outcome: the "/dashboard/server
genuinely remounts" reading (02-08, 02-VERIFICATION gap 1) was a proven
probe-measurement artifact, not a real defect — the generic
.view-container [data-controller-container] selector shared by every
KeepAlive-cached main tab couldn't disambiguate the foreground tab from
another still-connected cached tab, confirmed via an authoritative
document.elementFromPoint() hit-test that repeatedly contradicted the
naive "remounted" verdict across independent device runs for both Server
and Web5 (the latter a mid-investigation discovery). The committed
keepalive-remount-probe.spec.ts replaces the ad-hoc 02-08 probe, and four
new vm.$.uid-based regression tests pin real instance survival immune to
the same selector ambiguity.

Updates STATE.md (metrics, decisions, resolved blocker, session) and
ROADMAP.md (02-09 checked off, 9/10 plans executed) for phase 02's gap
closure. No code changes; 02-FINDINGS.md is owned by the concurrently
running 02-10 executor and was not touched here (already committed by
this plan's earlier Task 1/2 commits).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:09:53 -04:00
archipelagoandClaude Fable 5 3e3159fa95 test(02-09): pin Server/Web5 KeepAlive round-trip survival via instance uid
Demo images / Build & push demo images (push) Successful in 3m41s
Task 2 (no-op branch, per plan): Task 1's evidence positively proved
Server.vue and Web5.vue's instances already survive tab round-trips —
the "remounts" reading was a probe artifact (02-FINDINGS.md), not a real
defect. No change to DashboardRouterView.vue, dashboardViewWrappers.ts,
keepAliveRoutes.ts or Server.vue's KeepAlive/lifecycle wiring.

Lands 4 regression tests in keepAliveLifecycle.test.ts using Vue's own
component-instance identity (vm.$.uid) instead of a CSS selector, so the
pin can't inherit the same generic-.view-container ambiguity Task 1 found:
round-trip identity for Server (Test 1) and Web5 + a second tab (Test 2),
include-list correctness (Test 3), and the LRU cap staying intact (Test 4).
All four pass immediately against the unmodified code — that pass is
itself the pin, per the plan's explicitly anticipated no-change path.

Full suite green (95 files / 778 tests), type-check clean, build succeeds.
keepAliveTabs.test.ts confirmed byte-for-byte unmodified and still green.
No deploy: nothing in neode-ui/src changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:50:55 -04:00
archipelagoandClaude Fable 5 550a2927b9 chore(catalog): sign app-catalog — BotFights 1.2.2, iframe-embed fix (ARCHY_EMBEDDED)
releases/app-catalog.json regenerated and signed by the release-root key
(did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur). botfights is
the only content change vs the previously-published catalog (verified
structurally — all other 65 apps unchanged): 1.2.1 -> 1.2.2, carries the
X-Frame-Options fix (ARCHY_EMBEDDED=1 disables SAMEORIGIN for the
node-dashboard iframe) on top of 09-06's JWT_SECRET/arena-federation fixes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:46:24 -04:00
archipelagoandClaude Fable 5 4d471759a7 fix(02-09): name Server/Web5 KeepAlive "remount" as a proven probe artifact
Demo images / Build & push demo images (push) Successful in 3m36s
Task 1: built keepalive-remount-probe.spec.ts, a committed, re-runnable
Playwright spec covering every KEEP_ALIVE_PATHS tab with three added
instruments (instance-uid, session-wide console/pageerror capture, DOM
population/pathname logging per hop) beyond 02-08's ad-hoc probe.

Eliminated suspects 2 (runtime error), 3 (LRU eviction), 4 (route.path
mismatch) and 5 (include-name matching) by direct measurement. Confirmed
suspect 1 with positive proof: an authoritative document.elementFromPoint()
hit-test signal contradicted the naive selector-match method's "remounted"
verdict for both Server and Web5 across independent runs, and a companion
diagnostic found the original stamped root still connected+visible under
a different (unpicked) match. Root cause: Server, Web5 and Fleet share the
fully generic .view-container [data-controller-container] selector every
KeepAlive-cached main tab's root carries via fallthrough, which cannot
disambiguate "the foreground tab" from "another cached tab still connected
to the document" once more than one tab has been visited — the normal,
intended KeepAlive state. Settings (the away tab every round trip uses)
independently renders matching content too (AccountInfoSection/
KioskDisplaySection), compounding the ambiguity.

Server.vue and Web5.vue's instances survive tab round-trips exactly like
every other registered tab — no defect in DashboardRouterView.vue,
dashboardViewWrappers.ts, keepAliveRoutes.ts or Server.vue's KeepAlive
wiring. 02-FINDINGS.md records the full method, eliminated suspects and
verdict per D-10 (this commit lands before any src change, per gate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:42:54 -04:00
archipelagoandClaude Fable 5 3b3d22d0ce fix(botfights): 1.2.2 — allow node-dashboard iframe embedding via ARCHY_EMBEDDED=1
Demo images / Build & push demo images (push) Has been cancelled
1.2.x's auth-hardening work added Hono secureHeaders() with a default
X-Frame-Options: SAMEORIGIN, which unconditionally blocked the Archipelago
node dashboard's iframe (different origin by port) — a real regression
versus 1.1.0, which never sent this header. Fixed upstream in the botfight
repo (commit 8eb27ed): X-Frame-Options is now conditional on ARCHY_EMBEDDED,
disabled only for the first-party node-embedded instance.

apps/botfights/manifest.yml: image/version -> 1.2.2, adds
ARCHY_EMBEDDED=1 to environment, drops the interim
metadata.launch.open_in_new_tab workaround (no longer needed — the app can
now be framed). app-catalog/catalog.json, scripts/image-versions.sh,
neode-ui/public/catalog.json bumped in lockstep via
scripts/generate-app-catalog.py. core/archipelago/src/fips/app_ports.rs
regenerated (formatting only, same port set).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:40:18 -04:00
archipelagoandClaude Fable 5 a3b67c3d08 docs(roadmap): Phase 9 executed — all 7 plans complete, awaiting human demo verification
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:10:49 -04:00
archipelagoandClaude Fable 5 ec3ef2f7c2 docs(09-07): Task 1 complete + verified, Tasks 2/3 stopped at blocking human checkpoints
archi-dev-box BotFights 1.2.1 install fully automated-verified (image, arena federation,
per-install secret, data preservation, restart survival, arena unaffected). Tasks 2/3
(real NIP-07 signer login, real cloud-agent-from-prompt) require a human/external agent
per the plan's own design and 09-RESEARCH.md Pitfall 5 — stopped here rather than
simulated. STATE/ROADMAP/REQUIREMENTS intentionally left untouched per this run's
explicit instruction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:09:08 -04:00
archipelagoandClaude Fable 5 87b7b603fd feat(09-07): update archi-dev-box to BotFights 1.2.1 via signed catalog + demo checklist
Real user path: package.check-updates RPC (catalog refresh, release-root signature
verified) then package.update RPC (id=botfights) — no hand-placed container. Verified:
image :1.2.1 healthy, ARENA_UPSTREAM_URL set, JWT_SECRET delivered as a podman secret
(0600, 64 hex chars, no plaintext env value), local /api/bots matches the arena's 104+15
fighters (thin-client proxy confirmed), local botfights.db byte-identical pre/post
(367144960 bytes, mtime 1782916151 — 115 bots/102,440 fights preserved untouched),
survives podman restart, arena health unaffected. x250-dev unreachable this session
(SSH timeout) — dev-pair rule satisfied on archi-dev-box only, recorded in the checklist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 05:06:47 -04:00
archipelagoandClaude Fable 5 463134960c docs(09-06): BotFights 1.2.1 catalog signed and published — plan complete (BOT-04)
Signing ceremony ran successfully; catalog independently re-verified,
committed (a99522d0), and confirmed byte-identical to the content live at
the vps2 raw URL every node fetches. Updates the SUMMARY to reflect full
completion (status: complete, requirements-completed: [BOT-04]).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 04:50:20 -04:00
archipelagoandClaude Fable 5 a99522d0d4 chore(catalog): sign app-catalog — BotFights 1.2.1, generated JWT_SECRET fix + default-on arena
releases/app-catalog.json regenerated and signed by the release-root key
(did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur). The botfights
entry is the only content change vs the previously-published catalog
(verified structurally — all other 65 apps unchanged): version 1.1.0 ->
1.2.1, embeds the generated_secrets/secret_env JWT_SECRET fix (D-04/BOT-04,
prevents the 1.2.x image's crash-loop on missing JWT_SECRET) and default-on
ARENA_UPSTREAM_URL federation (D-03/BOT-03).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 04:46:58 -04:00
archipelagoandClaude fcbf50f67e docs(02-review): update fix commit hashes after rebase onto latest main
Demo images / Build & push demo images (push) Successful in 3m46s
Rebasing the review-fix branch onto main (which had advanced with
unrelated 09-06/botfights commits since this branch was created) rewrote
every commit hash. Update 02-REVIEW.md's Status lines and summary table to
reference the post-rebase hashes.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:18:10 -04:00
archipelagoandClaude 64aafa7899 docs(02-review): mark phase 02 review findings fixed/documented with commit hashes
CR-01 and WR-01 through WR-06 fixed (one commit each); IN-02 fixed
(trivial comment); IN-01 left documented, not fixed (requires a real
refactor across 4 files, outside this pass's trivial/zero-risk bar for
Info findings). Full test suite, vue-tsc --noEmit, and npm run build are
all green after every fix.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:40 -04:00
archipelagoandClaude b5506025dd fix(02-review): IN-02 document wrapperFor's currently-unreachable full-bleed branch
isFullBleedPath(path) only returns true for /dashboard/chat and
/dashboard/mesh, both always in KEEP_ALIVE_PATHS today, so this branch of
wrapperFor()'s key derivation is currently unreachable. Add a comment
(per the review's own "non-blocking; a comment is sufficient" fix) so a
future reader doesn't mistake the defensive branch for dead code to delete.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:39 -04:00
archipelagoandClaude 7e4e739e8d fix(02-review): WR-05 forward abort signal through vpnStatus()/dnsStatus()
server.network-summary's fetcher batches four RPCs but only two forwarded
the AbortSignal useCachedResource provides for abort-on-unmount;
rpcClient.vpnStatus()/dnsStatus() had no signal parameter at all, so
aborter.abort() couldn't cancel them, partially defeating the documented
abort-on-unmount contract for this resource.

Add an optional signal parameter to both convenience methods (mirroring
the pattern used throughout rpc-client.ts) and forward it from Server.vue.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:39 -04:00
archipelagoandClaude 5f7cd4c8bc fix(02-review): WR-04 require explicit persist on resources.ts entry()/optimistic()
entry(key, persist = true) and optimistic(key, update) silently defaulted
to persist:true after the first call for a key, and optimistic() didn't
accept a persist argument at all. Every current call site happened to be
safe, but the invariant was unenforced: a future caller invoking
store.optimistic() before any useCachedResource({persist:false}) has run
for that key in the same tick would silently start writing to
sessionStorage with no indication anything is wrong (T-02-01).

persist is now a required argument on both functions (no default), and the
per-key decision is recorded and asserted (dev-only warning) against any
later call that disagrees. useCachedResource's optimistic() wrapper now
threads its own already-resolved persist value through automatically, so
no existing composable caller changes behavior. The two call sites that
use the resources store directly (Cloud.vue/PeerFiles.vue's per-peer
browse cache) now pass persist:true explicitly, matching their existing
behavior exactly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:39 -04:00
archipelagoandClaude 69358bf6c7 fix(02-review): WR-03 never drop OpenWrtGateway Connect form params under concurrent load
load(params) routed every call -- including the Connect form's own
credentials -- through routerResource.refresh(), which resources.ts dedupes
per key. A second load({host, ssh_user, ssh_password}) call arriving while
an unrelated refresh was already in flight (e.g. useCachedResource's own
TTL-gated auto-revalidation) would just await that already-in-flight
promise; the caller's own params were silently never sent, with no error
surfaced.

load(params) now bypasses routerResource.refresh() entirely when explicit
params are supplied, calling rpcClient directly and writing the resolved
result into routerResource.entry so cache/TTL/status-panel rendering stays
consistent with a normal refresh() success. The plain reconnect path
(no params) is unchanged. The now-redundant pendingParams indirection is
removed since the fetcher only ever needs `{}` params going forward.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:38 -04:00
archipelagoandClaude 0486045d29 fix(02-review): WR-06 skip redundant fallback init timer on later MeshMap reactivations
armMapVisibility() unconditionally scheduled a setTimeout(initMap, 300) on
every reactivation, not just the first mount. initMap()'s own guard made
this harmless (a no-op once the map exists), but it scheduled a throwaway
timer on every tab-switch back into Mesh. Guard the scheduling itself so
intent ("fallback init for the very first mount") matches behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:38 -04:00
archipelagoandClaude 751b05f271 fix(02-review): WR-02 stop MeshMap geolocation watch on deactivate, resume on activate
onDeactivated only tore down the resize listener/ResizeObserver, not the
navigator.geolocation.watchPosition watch started by "Share Location" —
leaving GPS polling running in the background (battery drain, active
location indicator) for as long as the KeepAlive'd component survives,
instead of only while the Mesh tab is visible like every other resource
this phase added only-while-visible handling for in this file.

onDeactivated now stops an active watch (tracked via a flag rather than
losing the user's toggle state), and onActivated transparently resumes it
on return to the tab.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:38 -04:00
archipelagoandClaude 6105770420 fix(02-review): WR-01 decouple Discover's featured-banner data from app-catalog dedup race
Marketplace.vue and Discover.vue both register a useCachedResource against
the shared 'app-catalog' key with different fetchers; resources.ts's
in-flight dedup means whichever view's fetcher wins a given race governs
the shared entry, silently dropping Discover's catalogFeatured side effect
when Marketplace's simpler fetcher wins.

Give the featured-banner payload its own cache key ('app-catalog:featured')
subscribed only by Discover.vue, so it always gets its own data regardless
of which view's fetcher wins the shared 'app-catalog' race. fetchAppCatalog()
already memoizes internally (1h TTL + localStorage fallback), so this is
normally a cache hit rather than an extra network request. The shared
'app-catalog' key and its dedup behavior are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:38 -04:00
archipelagoandClaude 57989dfc58 fix(02-review): CR-01 web5.lnd-info/profits resources must not persist to sessionStorage
Web5.vue's lndInfoRes and profitsRes defaulted to persist:true (via
useCachedResource's default), writing live LND wallet balances and
channel balances to sessionStorage in plaintext -- a T-02-01 violation.
Add explicit persist:false to both, matching the "never defaulted"
rule this phase established everywhere else. Also updates Home.vue's
comment, which previously documented this as a known unfixed gap.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 04:17:38 -04:00
archipelagoandClaude Fable 5 921f450edf docs(09-06): Task 1 complete, signing ceremony (Task 2) pending — SUMMARY
Manifest/catalog prep for BotFights 1.2.1 is done and verified; the run
stopped intentionally before the human-only release-signing ceremony per
explicit instruction. releases/app-catalog.json is regenerated on disk but
deliberately not committed until it's signed (Task 3 continuation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 03:49:04 -04:00
archipelagoandClaude Fable 5 f281c7aad4 feat(botfights): bump manifest to 1.2.1 — generated JWT_SECRET + default-on arena federation
apps/botfights/manifest.yml: image tag 1.1.0 -> 1.2.1, adds container.generated_secrets
(botfights-jwt-secret, kind hex32) + secret_env (JWT_SECRET) so a fresh install no longer
crash-loops on the 1.2.x image's jwt.ts import-time throw when JWT_SECRET is unset under
NODE_ENV=production (D-04/BOT-04). Adds ARENA_UPSTREAM_URL=https://botfights.archipelago-foundation.org
to turn on shared public arena federation by default (BOT-03/D-03), with an in-manifest comment
documenting the per-node opt-out (remove the line to run standalone).

app-catalog/catalog.json + scripts/image-versions.sh bumped in lockstep to keep the legacy
catalog and the image-version drift checker consistent with the manifest.

releases/app-catalog.json intentionally NOT committed in this change — it has been
regenerated locally (scripts/generate-app-catalog.sh) with the new manifest embedded, but is
unsigned. It is committed separately (09-06 Task 3) only after the release-root signing
ceremony, so no unsigned catalog state ever lands even transiently on a branch this repo's
mirrors could serve to nodes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 03:38:16 -04:00
archipelagoandClaude 6904e78960 docs(02): create gap closure plans
Two plans closing the gaps 02-VERIFICATION.md found (6/8 must-have truths
verified):

- 02-09 (wave 6): Server.vue is registered in KEEP_ALIVE_PATHS since 02-04 but
  02-08's corrected, twice-reproduced probe shows it fully remounting on every
  revisit. Names the measured cause first (D-10), commits the ad-hoc probe as a
  re-runnable spec, lands a targeted fix pinned by a regression test in
  keepAliveLifecycle.test.ts, deploys frontend-only to archi-dev-box (D-15),
  and closes with the D-11 human pass bar.
- 02-10 (wave 7): the six surfaces measured unimproved/regressed vs baseline had
  a flagged-but-untested environmental-noise confound. Re-runs the frozen 02-01
  harness under recorded conditions, compares three-way using per-run sample
  spread rather than bare medians, and gives every named surface a verdict —
  cleared with data, fixed, or an explicit accepted deviation for the verifier.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 00:20:58 -04:00
archipelagoandClaude b448232fb5 docs(02): code review findings
Adversarial review of phase 02's KeepAlive/useCachedResource architecture:
1 blocker (Web5.vue wallet balances default-persisted to sessionStorage,
violating T-02-01) and 6 warnings (app-catalog cache-key race silently
drops Discover's featured banner, MeshMap geolocation watch survives tab
deactivation, OpenWrtGateway connect-form params droppable under
concurrent load, resources.ts persist-argument footgun, partial
abort-signal coverage on Server.vue's network-summary fetch, redundant
timer re-arm).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 00:11:52 -04:00
archipelagoandClaude 092a37332f docs(02): phase verification
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 00:03:13 -04:00
archipelago 2b89b78f18 docs(02-08): complete dev-pair deploy, on-device re-measure, D-11 pass bar plan 2026-07-30 23:55:05 -04:00
archipelagoandClaude Fable 5 f4ab25ef70 docs(09-05): build/push botfights:1.2.0, roll canonical arena, prove BOT-03 cross-instance visibility live
Image built + pushed (digest sha256:854ea299...26e144), canonical VPS2
arena rolled to 1.2.0 and verified end-to-end over the public HTTPS
URL (health, unified prompt, registration, bot auth, live SSE), and
BOT-03's cross-instance fighter visibility proven on real hosts in
both directions with a throwaway proxy-mode instance. Two pre-existing
bugs found and fixed in the botfight repo along the way: a pnpm
overrides config drift blocking the docker build, and a route-order
bug that made GET /api/fights/poll (the entire polling protocol)
always 404.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 23:40:51 -04:00
archipelagoandClaude Fable 5 a0c5827753 docs(02-08): confirm Cloud first-visit regression closed — 5/5 verified
Updates the Task 3 checkpoint follow-up addendum: the content.browse-peer
concurrency-cap fix (8fe6217b) resolved what the fresh-mount guard
(e1a3f31a) alone did not. Instrumentation showed 13 of 14 concurrent
browse-peer calls never settling; capping the fan-out at 3 concurrent
with a 10s per-call timeout eliminated the hang. Verified 5/5 fresh
sessions navigate cleanly on first folder click against the redeployed
build, zero in-flight requests after 15s on Cloud, and a previously-
hung route chunk import now resolves in 17ms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 23:40:16 -04:00
archipelagoandClaude Fable 5 8fe6217b5a fix(02-08): cap content.browse-peer fan-out — root cause of Cloud first-visit hang
Demo images / Build & push demo images (push) Successful in 3m29s
Instrumented the exact trigger the fresh-mount guard (previous commit)
didn't fully eliminate: on a fresh session, loadPeerFiles() fired one
content.browse-peer RPC per connected peer with zero concurrency cap
and a 30s per-call timeout. Confirmed on archi-dev-box: 13 of 14
concurrent browse-peer calls never settled at all (dead/unreachable
peers with no server-side timeout on that path) — that many
simultaneously open, indefinitely-pending same-origin requests starved
Chromium's connection pool, silently breaking every other same-origin
fetch for the rest of the session, including the lazy route chunk any
later folder/tab navigation needs. This — not the router or the click
handler — was the actual cause of "no folders open on click" after a
first Cloud visit.

Fix, mirroring PeerFiles.vue's existing PREVIEW_CONCURRENCY pattern for
the identical class of problem (content.preview-peer fan-out):
- Cap the browse-peer fan-out at 3 concurrent requests
  (BROWSE_PEER_CONCURRENCY, a queue+worker pool in loadPeerFiles()).
- Shorten each call's timeout from 30s to 10s (BROWSE_PEER_TIMEOUT_MS) —
  bounds how long any one dead peer can hold a connection.
- Wire an AbortController (aborted onUnmounted) through rpc-client's
  signal option for clean teardown.
- A timed-out/failed peer already resolved silently through
  resources.ts's own error-state path (no throw, no toast) — confirmed
  unchanged; the muted "N peers unreachable" line is the only surface.

No visual/behavioral change to the working case (D-01 rule) — peers
that answer still render exactly as before, just no longer share the
page with a dozen never-ending requests.

Full suite green (95 files / 774 tests), type-check and build clean,
keepAliveTabs.test.ts structural DOM assertions untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 23:33:32 -04:00
archipelago 91f17f5ab2 docs: pin UIFIX-04/05/06 todos with 02-08 classification pointers 2026-07-30 23:26:30 -04:00
archipelagoandClaude Fable 5 834edd8c00 docs(02-08): checkpoint follow-up — triage 5 user-reported items
Adds a Task 3 checkpoint follow-up addendum to FINDINGS.md documenting
the investigation behind the fix(02-08) commit (Cloud.vue fresh-mount
guard for the first-visit connection-pool stall — confirmed real,
partially fixed, not yet fully resolved) plus classification of four
other user-reported items, all traced via git history against the
a75b6709 pre-phase-2 baseline and confirmed pre-existing (not phase 2):
Paid Files opening images in a new tab instead of the lightbox, PiP not
closing the lightbox, missing loader state on Paid Files' item-open RPC,
and PiP not surviving tab changes/buffering pauses. No fixes applied
for the pre-existing items per standing direction (captured separately
as phase-1 UX work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 23:23:53 -04:00
archipelago bd6958255a docs: expand UIFIX-05 — PiP must survive tab changes and buffering 2026-07-30 23:21:51 -04:00
archipelagoandClaude Fable 5 e1a3f31a0a fix(02-08): Cloud.vue fresh-mount guard — first-visit connection-pool stall
Demo images / Build & push demo images (push) Successful in 3m32s
User-reported checkpoint regression: on a genuinely first visit to Cloud
this session, no folder opened on click (subsequent visits fine).
Root-caused on archi-dev-box, not guessed:

- The click DID register (confirmed via a direct DOM listener) and DID
  call router.push (confirmed by patching the live router instance) —
  but the push's promise never settled, because Vue Router awaits the
  target route's async component, and that dynamic import() itself
  never resolved.
- Confirmed via a manual import() from the page console: importing ANY
  lazy route chunk (Fleet, CloudFolder, AppDetails — unrelated views)
  hangs identically after visiting Cloud once, but works instantly
  before ever visiting Cloud. Not chunk-specific, not router-specific.
- Traced to exactly one permanently-pending network request: a File
  Browser `GET /app/filebrowser/api/resources/Photos` call from Cloud's
  own onMounted burst, confirmed hung via request-lifecycle tracking
  (never finishes or fails, still pending after 10s). The identical
  request, issued manually with a fresh token outside of Cloud.vue,
  returns in 29ms — ruling out the backend/File Browser itself.
- Mechanism: Cloud.vue's syncOnEntry() (loadCounts/loadPeers/
  loadPeerFiles) fires from BOTH onMounted and onActivated with no
  fresh-mount guard (02-04 exempted it, reasoning each resource is
  individually staleness/inflight-deduped — true per-resource, but the
  two back-to-back passes still double the concurrent request volume
  at the single riskiest instant in a session: first KeepAlive
  activation, stacked on whatever other cached view's own onMounted
  burst is firing at the same moment). On real hardware that volume
  was enough to leave one File Browser request stuck, which then
  starves Chromium's per-origin connection pool — breaking every
  subsequent same-origin fetch, including the lazy chunk any later
  navigation needs. Not a router or click-handler bug; the click and
  push both worked correctly the whole time.

Fix: give Cloud.vue the same fresh-mount guard already used in
Home.vue/Web5.vue/Mesh.vue/Server.vue (skip onActivated's redundant
first-activation re-fire since onMounted just ran it). Removes the
duplicate-burst mechanism without changing steady-state reactivation —
onActivated still re-syncs normally on every later KeepAlive round-trip.
No visual/behavioral change (D-01 rule).

Full test suite green (95 files / 774 tests), type-check and build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 22:44:35 -04:00
archipelago 8b1aac0174 docs(09-03): complete unified AI bot-setup prompt plan (BOT-02)
- SUMMARY for 09-03 (botfight repo commits bbc3c7a, a080956, 2dd9947)
- WINDOWS.md: record e2e suite as unrun-verify (local dev backend port
  9100 is occupied by the live archi-dev-box demo container)

Per explicit user instruction this run, STATE.md/ROADMAP.md/REQUIREMENTS.md
are intentionally left untouched (shared coordination files across
concurrent phase-09 agents).
2026-07-30 22:37:23 -04:00
archipelagoandClaude Fable 5 903481afb3 docs(09-02): complete BOT-01 bare-pubkey auth gap closure plan
GET /api/auth/me (JWT-only session restore) shipped, POST /login reduced
to a read-only deprecated lookup, client auto-restore moved off the bare
pubkey path. Includes a fixed pre-existing migrate.ts/schema.ts drift
(15 pre-existing auth/tournament test failures resolved as a deviation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 22:35:50 -04:00
archipelago 1fc006a05b docs: capture UIFIX-04/05/06 (paid-files lightbox, PiP animation, loader states) into phase 1 2026-07-30 21:59:47 -04:00
archipelagoandClaude Fable 5 f1206ad60f docs(02-08): re-measure every surface on archi-dev-box, commit Results/Outstanding
Re-ran the plan 02-01 harness unmodified against archi-dev-box (same
target, same runs:3 contract) producing 02-PERF-AFTER.json (15/15 rows,
matching the baseline row count). Appended FINDINGS.md's Results table
(per-surface baseline/after comparison, Verdict) and an Outstanding
list of every regression, unmeasured surface, and unsettled gap.

Discovered and cross-verified (via a corrected, harness-independent
remount probe, reproduced twice) that the harness's own remount-probe
selector ('.view-container', generic and shared across every main tab)
became ambiguous once real KeepAlive caching keeps multiple instances
alive simultaneously — the raw artifact's 'remounted' field is
confounded for main tabs whose instance actually survives. Corrected
readings show Home/Apps/Marketplace/Cloud/Web5/Fleet genuinely survive
a round-trip; Server genuinely does not (a real, confirmed gap against
this phase's own must-have truth, flagged in Outstanding, not fixed
here — out of this task's measurement-only scope).

Also recorded real timing regressions (Discover, Server, Web5, Fleet,
AppDetails, OpenWrtGateway) honestly rather than averaging them away,
per the plan's own prohibition, alongside a timing-variance caveat
(baseline and after-run were taken at different times of day on the
same multi-service node).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:48:09 -04:00
archipelagoandClaude Fable 5 4f51de9f81 docs(09): canonical arena URL is now https://botfights.archipelago-foundation.org (user-created DNS+NPM+LE)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:46:39 -04:00
archipelagoandClaude Fable 5 2ec9a70dfd docs(09-01): arena-proxy tracer complete — SUMMARY + deferred-items
Implements 09-01-PLAN.md (BOT-03 reverse-proxy tracer) end-to-end in the
botfight repo (pushed to origin main): server/src/middleware/arena-proxy.ts,
its 9-test end-to-end suite, app.ts mount, fights.ts SSE fix, docker-compose.yml
env-var docs. Records the T-09-02 threat-model correction (plain HTTP is a
supported upstream scheme, not https-only, per user decision) and a
pre-existing migrate.ts/schema.ts drift found out-of-scope during
verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:39:13 -04:00
archipelagoandClaude Fable 5 6a4043ce3d docs(09-04): complete canonical BotFights arena deployment plan
Records the VPS2 arena deployment (http://146.59.87.168:9100, plain HTTP
per user decision, full-copy data seed of archi-dev-box's botfights.db).
Note: this phase (09-botfights-platform-upgrade) is an out-of-band
user-directed express path, not tracked in this project's main
STATE.md/ROADMAP.md/REQUIREMENTS.md (currently on milestone v1.8.0 phase
02) — those files are intentionally left untouched to avoid clobbering
concurrent phase-02 execution tracking.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:28:25 -04:00
archipelagoandClaude Fable 5 3ee20430b0 docs(02-08): confirm KEEP_ALIVE_MAX=6 against on-device memory (FA-D)
Demo images / Build & push demo images (push) Failing after 2m18s
Deployed this phase's frontend + AIUI to archi-dev-box (the dev pair;
x250-dev is currently offline via Tailscale, recorded rather than
silently skipped) via scripts/deploy-to-target.sh --frontend-only.
Verified the served bundle (not just local dist) contains this phase's
changes.

Then took a real on-device measurement instead of leaving the FA-D
KEEP_ALIVE_MAX estimate unexamined: a headless Chromium session on
archi-dev-box cycled all 11 main tabs through 4 full round-trips (44
navigations), reading the JS heap via CDP before/after each cycle.
Memory fluctuated 10-21MB with no monotonic growth across cycles that
each exceed the cap 10 distinct registered paths against KEEP_ALIVE_MAX=6.
Left the constant at 6, now backed by a recorded measurement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:19:50 -04:00
archipelagoandClaude Fable 5 65d7edbd08 docs(09): lock arena-as-relay architecture decision (decentralized framing, Foundation arena = default rendezvous)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:11:04 -04:00
archipelagoandClaude Fable 5 de022b7b4e docs(09-04): resolve arena decisions — plain-HTTP :9100 endpoint (no DNS/TLS), full DB copy seed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:09:15 -04:00
archipelagoandClaude Fable 5 6a112797a5 docs(09): plan BotFights platform upgrade — 7 plans across 4 waves
BOT-01 native nostr signer login (GET /api/auth/me, bare-pubkey path retired),
BOT-02 one self-contained AI bot-setup prompt at /api/docs/prompt,
BOT-03 shared public arena on VPS2 with a node-side reverse-proxy tracer,
BOT-04 manifest 1.2.0 with a generated JWT secret + republished signed catalog.

Wave 1 runs 09-01..09-04 in parallel (app work plus the DNS/TLS human gate,
started early for the 2026-07-31 demo deadline); waves 2-4 build the image,
publish the catalog, and land the demo rehearsal on archi-dev-box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:03:09 -04:00
archipelagoandClaude Fable 5 9bead8fa4c docs(09): pattern map for BotFights phase
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:36:02 -04:00
archipelagoandClaude Fable 5 a3a77e965a docs(09): validation strategy + demo-deadline constraint for BotFights phase
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:33:42 -04:00
archipelagoandClaude Fable 5 b615a68617 docs(09): research botfights platform upgrade phase (nostr login, unified prompt, shared arena, catalog publish)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:32:04 -04:00
archipelagoandClaude Fable 5 32961dfc97 docs(09): phase 9 context — BotFights nostr login, unified prompt, VPS2 shared arena, registry update
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:21:06 -04:00
archipelago 5b37426fab docs(02-07): record live-testing follow-up round (overlay, theme, key fallback, CLI path) 2026-07-30 20:20:49 -04:00
archipelagoandClaude Fable 5 faf4a75ddf fix(02-07): loading overlay can never wedge the Chat/AIUI UI permanently
Demo images / Build & push demo images (push) Failing after 2m12s
Belt-and-suspenders fix on top of AIUI's own root-cause fix (the
archyBridge origin bug, fixed in the AIUI repo): the archy-side loading
overlay now gets pointer-events:none (it has no interactive content of
its own, so it should never have blocked clicks reaching the iframe
underneath) and a bounded 8s timeout that unconditionally hides it if
no 'ready' message ever arrives — regardless of AIUI/backend state.
The timeout only dismisses the overlay; it does not fabricate a
successful connection, so the connected indicator still reflects
reality.

Two new tests in chatAiuiEmbed.test.ts cover the timeout firing at
exactly 8s and not firing prematurely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:19:28 -04:00
archipelagoandClaude Fable 5 08ee5ed036 feat(seed): audit kernel CSPRNG readiness + RNG non-determinism regression test
Seed entropy comes from bip39 -> rand::thread_rng -> getrandom(2), which
blocks until the kernel pool is initialized -- but that ordering was
invisible in logs on first-boot ISO flows where the seed is generated
early. MasterSeed::generate() now probes getrandom(GRND_NONBLOCK) and
logs whether the pool was already seeded (warn if it would block).

Also adds a regression test that 64 generated mnemonics are all unique
with sane word diversity, guarding against a fixed/seeded RNG ever
being wired into seed generation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:18:32 -04:00
archipelagoandClaude Fable 5 35f6e75e83 docs(roadmap): add Phase 9 - BotFights Platform Upgrade (nostr signer login, unified AI prompt, VPS2 shared match endpoint, registry update)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 20:15:30 -04:00
archipelago ee8e60a37c docs: commit phase pattern maps + discuss checkpoint for portability 2026-07-30 20:14:03 -04:00
archipelago 62c99a60b2 docs: add UIFIX-01/02/03 blockers to phase 1 requirements and criteria 2026-07-30 19:06:58 -04:00
archipelago b2c6fea4d3 docs: record companion/fips subdomains in VPS2 migration task 2026-07-30 18:58:10 -04:00
archipelago a41207eeab docs: full VPS2 domain-migration inventory + verified coverage matrix 2026-07-30 18:49:53 -04:00
archipelago d677f6f958 docs: capture todo - migrate source references to HTTPS domain 2026-07-30 18:46:39 -04:00
archipelago 61e95273e0 docs(02-07): complete Chat/AIUI embed stability + D-14 plan 2026-07-30 18:44:04 -04:00
archipelago 5bdb3e6268 docs: capture todo - onboarding tickbox hidden below fold on short screens 2026-07-30 18:32:18 -04:00
archipelago 99ecb95f53 docs: capture todo - connected-nodes list must scroll at row-matched height 2026-07-30 18:30:20 -04:00
archipelagoandClaude Fable 5 e2b2ade3b2 feat(02-07): stable AIUI embed URL carrying both D-14 defaults
Demo images / Build & push demo images (push) Failing after 2m16s
aiuiUrl now appends chatExpanded=true and mobileChat=true alongside
the pre-existing embedded=true and hideClose=true. Both are static
strings with no reactive dependency, so the computed's value never
changes after first evaluation — preserving the URL-stability
contract 02-04 relies on to keep the AIUI iframe from reloading on a
tab switch.

AIUI reads these two flags (commit 900c0b9 in the AIUI checkout,
recorded in 02-AIUI-D14.md) to start the chat expanded and, on
mobile, on the chat view rather than the context view. The deployed
AIUI build on any node does not yet carry that commit (anonymous push
to the AIUI remote was rejected — see 02-AIUI-D14.md's Deployment
Impact), so neode-ui's two new query params are inert no-ops against
today's deployed AIUI until that commit is merged and shipped; both
flags are additive and harmless in the meantime.

New test file chatAiuiEmbed.test.ts covers: both D-14 flags plus the
pre-existing embedded/hideClose params present in the URL; URL
string-equality across a simulated viewport resize and across a
KeepAlive deactivate/reactivate cycle; onAiuiMessage still rejecting
a foreign-origin message; and aiuiConnected surviving a
deactivate/reactivate cycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 18:26:52 -04:00
archipelago 8d2ce96eee docs: add FED-07 fedimint gateway default-password blocker (phase 1) 2026-07-30 18:26:04 -04:00
archipelago 76b0cd8844 docs: capture todo - Keep FIPS/Tor pills on cloud files and show them on mobile 2026-07-30 18:20:42 -04:00
archipelagoandClaude Fable 5 71b2703213 docs(02-07): resolve AIUI source location and record real D-14 contract
AIUI is now cloned at /home/archipelago/Projects/AIUI
(git.tx1138.com/lfg2025/AIUI, development branch — 17 commits ahead
of main with zero unique main commits). Replaces the exhausted-search
conclusion with the real embed-parameter contract read from source
and records the D-14a/D-14b implementation: chatCollapsed's initial
state now honors a new ?chatExpanded param (chat.ts), and ChatPage.vue
re-asserts mobileTab='chat' on mount when ?mobileChat is present,
guarding against carry-over from AIUI's own module-singleton content-
panel state on an internal remount. Both are static, unconditional
query params — no runtime-varying URL input.

AIUI-side commit lives on feat/d14-embed-defaults (900c0b9) in the
local clone only — anonymous push to origin returned 403 Forbidden.
Needs a maintainer with push rights to land it before any deployed
AIUI build can honor these two parameters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 18:19:17 -04:00
archipelagoandClaude Fable 5 c10f415c6b docs(02-07): record AIUI source location and D-14 embed contract
Task 1 of plan 02-07. Searched the expected sibling AIUI/ checkout
(both relative-path forms neode-ui/package.json and
scripts/setup-aiui-server.sh reference), a broader filesystem sweep
for any directory named AIUI holding source rather than a prebuilt
dist/, and the ThinkPad build server (.116) noted in project memory
— all three came up empty (no sibling checkout, no other source dir,
.116 unreachable via ping/ssh right now).

Recorded the existing embed-parameter contract from Chat.vue plus
diligence findings from the currently-deployed AIUI build's static
bundle (embedded/mockArchy/hideClose support), and marked both D-14
defaults as needing a new AIUI-side parameter or postMessage control
that cannot be added without source access. Deployment impact section
covers the prebuilt-image rebuild path per apps/aiui/manifest.yml and
D-15's dev-pair-only constraint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 17:58:05 -04:00
archipelago 8205e9f142 docs(02-06): complete Server and Home tab cache plan 2026-07-30 17:42:55 -04:00
archipelagoandClaude Fable 5 926fa60691 feat(02-06): cache Home's system/update/storage groups and guarantee wallet freshness on re-entry
Demo images / Build & push demo images (push) Failing after 2m29s
- System stats (homeStatus.refresh), update status and cloud storage usage
  are now every-entry, TTL-gated useCachedResource entries (10s/300s/30s),
  hosted in Home.vue rather than inside the homeStatus Pinia store — a
  store's own defineStore(id, setup) runs in a bare effectScope where
  onActivated() silently no-ops (same finding as 02-05's Mesh.vue)
- Wallet is the deliberate exception (T-02-13): a new home.wallet-status
  resource wraps the existing loadWeb5Status() composite fetch and
  revalidates UNCONDITIONALLY on every activation rather than TTL-gated,
  keeps the prior figure rendered throughout, and persist:false (never
  written to sessionStorage). hydrateWalletSnapshot()'s separate localStorage
  path is untouched.
- Read Web5.vue's two existing resources (web5.networking-profits,
  web5.lnd-info) and did NOT share either key: profits is an unrelated
  dataset, and lnd-info's own default persist:true (Web5.vue out of this
  plan's file scope) would leak balance data via its own independent
  refresh cycle regardless of what Home declares, and Home's wallet fetch
  is a strictly broader 7-call composite (not the same single-call dataset)
- dedup:true added to all 12 underlying rpcClient calls (Home.vue's wallet
  composite + checkUpdateStatus, homeStatus.ts's 5 status calls)
- RefreshIndicator wired next to the Home header, bound to the wallet
  resource's loadState
- The websocket wallet-push path and hydrateWalletSnapshot's pre-network
  paint are both left exactly as 02-04 placed them

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 17:38:01 -04:00
archipelagoandClaude Fable 5 e6ed5536c9 feat(02-06): cache the Server tab's seven load groups with explicit TTL/persist
Demo images / Build & push demo images (push) Failing after 3m46s
Server.vue already had five of the seven load groups (network summary, FIPS
summary, VPN peers, interfaces, Tor services) on useCachedResource from a
pre-phase commit, but none declared an explicit ttlMs/persist (relying on
the composable's 30s/persist:true defaults) and dedup:true was missing from
several underlying RPC calls. loadDiskStatus was the one remaining plain
uncached fetch, forced on every activation.

- Explicit TTL per group (10s fast tier: network-summary/interfaces/
  disk-status; 30s near-default: vpn-peers/tor-services; 60s near-static:
  fips-summary)
- Explicit persist:false for server.vpn-peers (npub/peer identity) and
  server.tor-services (onion addresses) per T-02-01; other groups persist
- New server.disk-status cached resource replaces the plain fetch; it now
  self-heals via the composable's own onActivated instead of an explicit
  every-entry call
- dedup:true added to all underlying rpcClient calls, including the
  parameterless vpnStatus()/dnsStatus()/diskStatus() convenience methods
- RefreshIndicator wired into a new minimal header row, driven by whether
  any of the six cache entries is refreshing
- RESEARCH assumption A3 settled: read all seven loader bodies; none
  consumes another's result — the concurrent fan-out is correct as-is
- Fixed a keepAliveLifecycle.test.ts assertion invalidated by the new 10s
  network-summary TTL (reactivation now also revalidates that resource,
  adding one more vpnStatus() call the test didn't previously account for)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 17:03:55 -04:00
archipelago 8563cf4a84 docs(02-05): complete Mesh tab cache + graphics lifecycle plan 2026-07-30 16:25:06 -04:00
archipelagoandClaude Fable 5 abdfa07a77 feat(02-05): bound the Leaflet map's lifecycle across Mesh tab deactivation
Demo images / Build & push demo images (push) Failing after 3m45s
MeshMap.vue's onMounted setup (window resize listener + ResizeObserver)
is refactored into an idempotent armMapVisibility()/disarmMapVisibility()
pair, dual-registered on both onMounted and onActivated (onActivated is a
documented no-op outside a KeepAlive boundary) and torn down on
onDeactivated, matching the arm/disarm idiom Mesh.vue itself already uses.
The Leaflet instance is never destroyed or recreated by this — initMap()'s
own `if (!mapContainer.value || map) return` guard already makes
construction idempotent, so exactly one map is built per session. On
reactivation the map's size is invalidated via nextTick so a map laid out
while off screen re-tiles at its real size instead of showing an unsized
or partially tiled canvas.

FLAGGED: RESEARCH.md's premise that Mesh.vue owns a live D3 force
simulation does not hold for this codebase — a grep for
d3/forceSimulation/simulation across neode-ui/src found nothing in
Mesh.vue's or MeshMap.vue's tree; the only D3 force simulation belongs to
NetworkMap.vue (Federation.vue's graph, out of this plan's scope). The
plan's D3-specific truths are therefore vacuously satisfied — see
02-05-SUMMARY.md for detail. Only the real Leaflet-map lifecycle work
landed here.

meshMapLifecycle.test.ts is a new, separate file (not appended to
meshTabCache.test.ts) because its vi.mock('@/stores/mesh')/vi.mock('leaflet')
hoist file-wide and would otherwise clobber meshTabCache.test.ts's need for
the real mesh/transport stores — mirrors the MarketplaceRefresh.test.ts
precedent from 02-02 for the same class of vi.mock-hoisting conflict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 16:16:40 -04:00
archipelagoandClaude Fable 5 31389bcc3c feat(02-05): cache the six Mesh tab-entry fetch groups without serializing them
- Mesh.vue: six useCachedResource entries wrap mesh.refreshAll(),
  transport.fetchStatus(), refreshFederationNodes(), refreshSelfOnion(),
  refreshSelfDid() and refreshContacts(), each with an explicit TTL
  (10s reachability/transport, 30s federation/contacts, 300s self
  DID/onion) and persist decision (T-02-01: every group carrying peer
  or self identity data is persist:false; only aggregate transport
  status may persist)
- armMeshLive's Promise.all fan-out becomes a single Promise.allSettled
  over refreshMeshGroupIfStale() per group, so a revisit inside TTL
  issues zero RPC, a stale revisit revalidates concurrently (not a
  serial chain, T-02-16), and one rejected group never blocks the rest
- RefreshIndicator wired into the Mesh header, driven by whether any of
  the six groups is refreshing — visible while peer reachability
  revalidates on re-entry so a resumed tab never shows a frozen
  reachability state as current (T-02-13)
- dedup: true added to every underlying rpc-client.ts/mesh.ts/
  transport.ts fetcher call backing the six groups
- useCachedResource() calls live in Mesh.vue rather than inside
  stores/mesh.ts or stores/transport.ts: Pinia's defineStore(id, setup)
  runs in a bare effectScope, not a component instance, so the
  composable's internal onActivated() would silently no-op there; the
  fetchers still wrap the stores' own actions unchanged, so those
  actions' other callers (clearAllMesh, setMeshOnly, the pre-send
  balance check) keep their guaranteed-fresh, uncached reads

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 16:14:32 -04:00
archipelago e582124259 docs(02-04): complete main-tab lifecycle audit plan 2026-07-30 15:31:21 -04:00
archipelagoandClaude Fable 5 03a3e4e0c1 feat(02-04): widen KEEP_ALIVE_PATHS to the full audited main-tab set
Demo images / Build & push demo images (push) Successful in 3m49s
Task 2 of 02-04 — finishes the remaining tabs' lifecycle audit (Apps.vue,
Discover.vue) and widens registration from the 02-02 tracer's single seed
path to every main tab the profiling pass showed remounting.

- Apps.vue: the 15s "unable to connect" timer follows activate/deactivate
  (idempotent re-arm, cleared on exit) and resets connectionError on entry so
  a since-reconnected node doesn't show a stale error instantly; the intro
  flag stays once-per-session.
- Discover.vue: loadCommunityMarketplace/loadBitcoinPruneStatus now route
  through the same shared 'app-catalog'/'bitcoin.prune-status' cache keys
  Marketplace.vue introduced in 02-02, rather than duplicating the fetch;
  RefreshIndicator wired to the catalog resource's loadState. Discover's own
  dynamic-catalog-first fetcher (fetchAppCatalog with a curated-list
  fallback) is preserved as this key's fetcher for this view — both views
  are valid producers of the same shared cache entry.
- Fleet.vue: confirmed no lifecycle side effects (grep for the five tokens
  found none) — left unchanged, registered as-is.
- keepAliveRoutes.ts: KEEP_ALIVE_PATHS now derives from TAB_ORDER (single
  source of truth) plus /dashboard/discover, deliberately withholding
  /dashboard/settings even though it's in TAB_ORDER — Settings.vue's child
  sections (SystemDangerZone's reboot poll interval,
  VpnStatusSection/KioskDisplaySection/TransportPrefsCard/ClaudeAuthSection's
  one-shot onMounted fetches) were never in this plan's file scope and would
  misbehave under KeepAlive exactly as this plan exists to prevent. Every
  other TAB_ORDER path measured Remounted:true or was unmeasured in
  02-FINDINGS.md, so per the plan's literal exclusion rule (only a measured
  Remounted:false excludes) they all stay registered, including Mesh and
  Chat.
- keepAliveLifecycle.test.ts extended (in the prior commit) with
  shouldKeepAlive true/false assertions across every registered path and six
  secondary-screen paths, plus the KEEP_ALIVE_MAX+2 eviction test against the
  real DashboardRouterView + KEEP_ALIVE_PATHS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 15:06:03 -04:00
archipelagoandClaude Fable 5 f177a505b4 feat(02-04): main-tab side effects placed for activate/deactivate lifecycle
Demo images / Build & push demo images (push) Has been cancelled
Task 1 of 02-04 — audits every side effect owned by Home.vue, web5/Web5.vue,
Chat.vue, Cloud.vue, Server.vue and Mesh.vue and places each into one of
three buckets (once-per-session, every-entry, only-while-visible) so their
instances are safe to keep alive once KEEP_ALIVE_PATHS widens in Task 2.

- Home.vue: systemStats/wallet polling, the wsClient wallet-push
  subscription and its debounce timer follow activate/deactivate with an
  immediate re-sync on entry; hydrateWalletSnapshot/checkUpdateStatus/cloud
  usage stay once-per-session.
- Chat.vue: the window `message` listener and ContextBroker follow
  activate/deactivate; aiuiConnected is never reset on deactivate since the
  iframe's one-time 'ready' message won't resend on re-entry.
- Web5.vue: the six child-component data loaders (none use
  useCachedResource internally) and the 30s LND poll move to
  activate/deactivate; the DID lookup and intro flag stay once-per-session.
- Cloud.vue: the per-peer transport/reachability warm-cache
  (loadPeerFiles/loadCounts/loadPeers) re-runs every entry — the one path
  here that bypasses useCachedResource and would otherwise render stale peer
  reachability (T-02-13).
- Server.vue: the previously module-scope-armed 15s VPN poll interval now
  follows activate/deactivate (it used to run forever regardless of
  visibility); loadDiskStatus becomes every-entry.
- Mesh.vue: the entire live-communications surface (window/document
  listeners, the 5s/15s poll intervals, the ws peer-push subscription, and
  the six-way federation/self/contacts refresh) follows activate/deactivate;
  a share-to-mesh handoff via direct navigation is now correctly picked up
  on every activation, not just the first mount.
- useCachedResource.ts: onActivated's staleness check now skips an
  `immediate: false` resource that has never been explicitly fetched, so a
  tab-gated lazy resource (Cloud.vue's Paid Files / My Files walk) isn't
  eagerly force-loaded the moment its owning view is kept alive.
- Every arm/disarm pair is idempotent and duplicated into both onMounted and
  onActivated, since onActivated is a no-op outside a KeepAlive boundary
  (caught by CloudPeersRefresh.test.ts, which mounts Cloud.vue bare) —
  fresh-mount guard flags avoid double-firing the heavier loaders
  (Home/Mesh/Web5/Server) on a KeepAlive-wrapped first mount.
- New neode-ui/src/views/dashboard/__tests__/keepAliveLifecycle.test.ts
  covers the six lifecycle behaviors plus a real-view assertion
  (Server.vue's VPN poll, mounted inside a real KeepAlive).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 15:04:41 -04:00
archipelago a579556a4f docs(02-02): complete tracer tab plan 2026-07-30 09:22:58 -04:00
archipelagoandClaude Fable 5 2668705516 fix(02-02): restore page margins and slide transitions broken by KeepAlive restructure
Demo images / Build & push demo images (push) Successful in 3m36s
The Task 3 checkpoint failed on the real preview: outer page margins broke
and the up/down main-tab slide animations stopped playing. Root cause: the
02-02 restructure moved view-wrapper (absolute inset-0) onto each view's
root inside the padded wrapper, and split navigation across two sibling
Transitions behind a stable intermediate div — but dashboard-styles.css
scopes every transition as a compound selector on .view-wrapper, which must
be the keyed direct child of .perspective-container.

- Restore the pre-02-02 rendered DOM exactly: single Transition whose child
  is a keyed div.view-wrapper containing the per-route wrapper shape
- Re-integrate KeepAlive via statically-named per-route wrapper components
  (dashboardViewWrappers.ts) so the keyed div.view-wrapper is the cached
  component's own root; cache membership gated by :include on wrapper names
  derived from KEEP_ALIVE_PATHS
- Drop the scroll-retention Map: scroll containers now live inside keyed/
  cached wrappers, restoring the old reset-to-top behavior for non-kept routes
- Pin the visual contract structurally in keepAliveTabs.test.ts (padded
  wrapper classes must render INSIDE div.view-wrapper)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 08:40:27 -04:00
archipelago ac6852893d docs(02-03): complete secondary screen caching plan 2026-07-30 08:27:49 -04:00