2092 Commits

Author SHA1 Message Date
archipelago
fd361fb35e test(update): serialize the apply_update regression tests
Both tests take the global single-flight UPDATE_OP_LOCK; run concurrently
by the test harness, one saw the other's lock and failed with 'another
update operation is already running' instead of its expected refusal.
A shared test mutex makes them mutually exclusive deterministically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 02:01:58 -04:00
archipelago
8bb61a51e2 style: cargo fmt update.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:32:57 -04:00
archipelago
17d225190a docs: v1.7.104-alpha changelog + What's New sync
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m42s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:31:12 -04:00
archipelago
d08c0d29c7 fix(ui): ElectrumX sync screen shows the app's icon instead of a generic glyph
Some checks failed
Demo images / Build & push demo images (push) Has been cancelled
The pre-UI sync overlay used a hardcoded orange box-glyph SVG; it now renders
the same resolved app icon the loading screen uses (with the shared image-error
fallback), so the wait page reads as ElectrumX at a glance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:29:46 -04:00
archipelago
a93bd70c5a fix(electrumx): probe bitcoin-knots/bitcoin-core for the daemon host instead of hardcoding knots
The manifest baked bitcoin-knots:8332 into DAEMON_URL. Nodes whose
backend runs under the bitcoin-core name (and any future variant
without a knots DNS alias) left electrumx permanently disconnected —
'connection problem' forever and a block index stuck at 0. The
startup script now picks the first backend name that resolves on
archy-net and falls back to bitcoin-knots.

Catalog regenerated (catalog manifests override disk ones fleet-wide);
this regen also embeds the recently-merged barkd/Ark manifest for the
first time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:55:06 -04:00
archipelago
25162ee846 feat(update): auto-rollback OTA binaries that crash-loop before they can self-verify
.198 crash-looped 236x on a truncated OTA binary with a good backup
sitting in update-backup/ — verify_pending_update() can't help when
the new binary never runs. New scripts/ota-crash-guard.sh runs as
root from ExecStartPre: while the post-OTA pending-verify marker
exists it counts start attempts, and after 5 failures restores the
backup binary (atomic rename), replaces the marker with an
update-rolled-back.json tombstone, and lets the service come back
on the previous version.

Wiring: fresh ISOs get the ExecStartPre line in the unit file;
existing nodes get a drop-in installed by apply_update's runtime
component step on their next OTA. '+-' prefix so a missing or
failing guard can never block the service.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:42:54 -04:00
archipelago
837cfdfd1f fix(update): never apply unverified staging — close the OTA truncated-binary race
Root cause of .198 bricking on the 1.7.103 OTA: two concurrent
update.download RPCs shared one staging file, cancel_download wiped
staging mid-flight, a third download began re-filling it, and
apply_update mv'd the 3-second-old 17MB partial of the 49MB binary
into /usr/local/bin -> SEGV boot loop (236 restarts, no rollback).

- Single-flight UPDATE_OP_LOCK across download/apply; concurrent
  callers get an explicit 'already running' error.
- apply_update now requires the .download-complete marker AND
  re-verifies every staged component (size + SHA-256 + BLAKE3)
  against the manifest before touching the system.
- cancel_download only wipes staging when no operation holds the
  lock; otherwise it just flags the in-flight loop to bail.
- Fixed the 'file already complete' path in
  download_component_resumable, which skipped verification and fell
  through to the bogus 'download failed without a captured error'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 19:33:52 -04:00
archipelago
573b469191 chore: sign v1.7.103-alpha OTA manifest
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m53s
Also folds the Cargo.lock version bump that create-release.sh missed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v1.7.103-alpha
2026-07-18 07:48:56 -04:00
archipelago
401f92a24f chore: release v1.7.103-alpha 2026-07-18 07:30:25 -04:00
archipelago
dc0adbef70 docs: v1.7.103-alpha changelog + What's New sync
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 07:01:45 -04:00
archipelago
537c9fa70b fix(demo): suppress the PWA update prompt + reload on the public demo
The SW-update modal is noise on the demo (nothing to update to) and both
accept-paths end in a reload that replays the intro from scratch. With
skipWaiting/clientsClaim off, ignoring the waiting worker is safe — the
new build activates on the visitor's next session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 07:01:45 -04:00
Dorian
b6468ebf3c fix(android): let Vue re-render before auto-login submits the password
Dispatch the Enter keydown two frames after the input event so the login
button is enabled when it arrives. Keeps auto-login working against nodes
running older web builds where controller-nav's Enter-in-input pattern
would otherwise click Replay Intro (see the matching web-ui fix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:54:12 +01:00
Dorian
70587210fb fix(ui): stop controller-nav Enter from clicking Replay Intro on auth inputs
The companion's auto-login fills the password and dispatches Enter in the
same tick, before Vue re-enables the disabled submit button. Controller-nav's
'Enter in input clicks the next enabled button' pattern then hit the Replay
Intro button — clearing neode_intro_seen and hard-navigating to /, so every
app connect replayed the intro cinematic in a loop. The auth inputs all have
their own Enter handlers, so they opt out via data-controller-no-submit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 11:54:12 +01:00
archipelago
a27c7bafbf chore: sign v1.7.102-alpha OTA manifest
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m53s
Also folds the Cargo.lock version bump that create-release.sh missed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v1.7.102-alpha
2026-07-17 16:26:46 -04:00
archipelago
95b9d8f0fe chore: release v1.7.102-alpha 2026-07-17 12:36:44 -04:00
archipelago
918aba1de3 docs: v1.7.102-alpha changelog + What's New sync
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m50s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 12:22:41 -04:00
archipelago
49b366fbe4 test(goals): align goal-status tests with manual-step completion semantics
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m47s
3aebbcbb made goal completion require walking the manual steps (running
apps alone no longer finish a goal) but left the store tests asserting
the old auto-complete behavior. Tests now walk the manual step and also
pin the new running-but-not-walked => in-progress case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:46:38 -04:00
archipelago
7eaf99873e chore: cargo fmt — settle formatting drift blocking the release gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:41:31 -04:00
archipelago
a76a92cff8 fix(iso): zstd-compress bundled core container images (-160MB on the ISO)
Bundling fmcd for offline installs (6dcdada3) shipped the raw
uncompressed podman save tar and grew the ISO ~220MB (2.3G -> 2.5G in
RC6-RC9). Save the core bundle as .tar.zst instead — podman load
auto-detects compression, verified locally against the RC9 fmcd.tar
(228M -> 65M, loads cleanly into rootless storage). The first-boot
loader and installer copy globs now pick up .tar.zst too.

Also trims the installer's USB->disk image copy and first-boot load
time on fresh installs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:31:43 -04:00
archipelago
a177ef3b38 fix(kiosk): clear stale Chromium profile lock so rename doesn't kill the kiosk (#98)
Chromium's SingletonLock is a symlink encoding <hostname>-<pid>. After a
node rename changes the OS hostname, the lock left from the previous
boot reads as "another computer" holding the profile; Chromium refuses
to start, --noerrdialogs hides the only dialog, and the kiosk
black-screens forever.

Two layers:
- kiosk launcher removes Singleton{Lock,Cookie,Socket} before every
  Chromium start (any prior owner is dead at that point — pkill at
  session start / loop respawn). Ships via ISO splice AND the binary's
  include_str! self-heal, so existing nodes get it with the next OTA.
- the rename handler clears the lock immediately as a hostname
  side-effect, covering nodes still running a pre-fix launcher.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:31:43 -04:00
archipelago
ea0cd87b3b fix(auth): sync OS login password when the UI password is set at install (#97)
auth.setup only wrote the web password hash to user.json, so the
archipelago system user kept the image default password ("archipelago")
on console/SSH even after the user picked a real password during
onboarding. Reuse the existing usermod-based sync (already used by
auth.changePassword's alsoChangeSsh path) at setup time, best-effort so
a failure can never break onboarding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:31:27 -04:00
archipelago
1ee1b56f70 fix(ui): desktop Tor rows get their inline actions back + card actions bottom-align across grid cards
Some checks failed
Demo images / Build & push demo images (push) Failing after 1m55s
The card-action sweep put the mobile 50/50 Delete|Rotate row under every
Tor service on desktop too — desktop reverts to the original compact
inline Rotate / trash / toggle beside each row, while mobile keeps the
thumb-safe stacked layout. The VPN and Network Interfaces cards' bottom
buttons now pin to the card bottom (mt-auto) so they stay vertically
aligned when the grid stretches one card taller than the other.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 23:15:44 -04:00
archipelago
73d181abea fix(setup): wizard navigation round-trips — back to Setup tab, goal-aware channels back button, configure launches the app
Some checks failed
Demo images / Build & push demo images (push) Failing after 1m56s
- "Back to Goals" now returns to Home's Setup tab (?tab=setup) instead of
  landing on the dashboard tab.
- The channels screen remembers when a setup wizard sent you there
  (?from=goal) — its back button reads "Back to Setup" and returns to the
  wizard; it also now uses the shared BackButton pill instead of a bare
  link.
- "Open & Configure" steps launch the actual app via the app launcher —
  iframe apps overlay on top of the wizard, tab-only apps (BTCPay,
  Nextcloud) open a tab, mobile uses the in-app browser — instead of
  routing to the app-details page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:16:58 -04:00
55d7f19545 Merge pull request 'feat(ui): Networking Profits dashboard + card-action consistency sweep' (#96) from networking-profits-dashboard into main
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m46s
2026-07-17 02:08:02 +00:00
Dorian
46dd853614 fix(ui): heavier scrim over the bright web5/server backdrop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:07:30 +01:00
Dorian
977b8d06b8 fix(ui): Connect to Mesh routes to /dashboard/mesh, not the 404 /mesh
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:07:30 +01:00
Dorian
f08f34ca10 fix(ui): card actions live at the bottom on every screen size
Retire the >=1800px compact-header variant globally (Monitoring Details,
Federation Find Nodes/Fleet, Identities Create, VPN Add Device, Network
Interfaces Scan WiFi, Tor Restart/Add Service). Tor service rows get a
50/50 Delete|Rotate row underneath — Delete on the left, away from the
enable toggle, so no missclicks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:07:30 +01:00
Dorian
aaa3477d5e feat(ui): Networking Profits becomes a dashboard
Dashboard tab (default): stat tiles per earning source, 7-day earnings
LineChart (one line per source, daily buckets — earnings are sparse
events so finer buckets would draw noise), active-session count and
revenue-by-service bars from streaming.list-sessions. Configure tab
keeps the old settings screen with the intro copy boxed in a card, plus
a new TollGate WiFi entry (price per minute via openwrt.provision-
tollgate, with a set-up-gateway hint when no router is paired). Demo
mocks gain profit history entries and streaming sessions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:07:30 +01:00
archipelago
9e264611e2 docs: Nostr signer-login research + identity-import UX plan
Research report on the most frictionless "sign in with your Nostr signer"
flow for node login (NIP-07 extension + NIP-46 QR scan with Amber, password
always kept as fallback), and a plan for adding existing Nostr identities
(nsec import / npub watch-only / browser extension) to the Nostr Identities
screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:59:54 -04:00
archipelago
f32c4db7e2 style(ui): companion banner uses the Setup-tab hero image
Some checks failed
Demo images / Build & push demo images (push) Failing after 3m10s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:51:12 -04:00
archipelago
8e13f981d0 fix(demo): IndeeHub opens its real site — the same-origin proxy broke its assets
The nginx sub_filter rewrite couldn't touch the SPA's runtime-built asset
URLs, so the iframed IndeeHub rendered with broken links and images. The
real site refuses iframing (X-Frame-Options), so the demo now opens
https://indee.tx1138.com/ directly via the demo-external mechanism, whose
isDemoExternal() check was previously hardcoded off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:51:12 -04:00
archipelago
3aebbcbbb8 feat(setup): Zeus lightning journey — fund-wallet step, IBD timer + finish-setup toast, channel suggestions
Every Lightning setup (Open a Shop, Accept Payments, Run a Lightning Node)
gains a guided path to a working channel:

- New "Fund Your Bitcoin Wallet" step, gated on the blockchain finishing
  its initial sync: while syncing it shows a live progress bar with a
  counting-down time-remaining estimate; once synced it shows the on-chain
  balance and a "Fund Wallet" button that opens the receive modal with a
  fresh address, QR, and the Zeus channel limits (min 150,000 / max
  1,500,000 sats) noted.
- When the sync finishes while a Lightning setup is mid-flight, a toast
  pops with a "Finish setup" link straight back to the wizard (toasts now
  support action links). If several setups are in flight, one is chosen —
  the shared steps complete the lightning part of any of them.
- Channel steps are Zeus-branded (logo + copy) and land on the Lightning
  Channels screen, which now carries an "Open a channel with Zeus" card
  that prefills the open-channel modal with the Olympus peer URI, 150k
  sats, and private-channel checked. "Get Zeus" links to zeusln.com.
- Setup completion now actually requires walking the manual steps (fund,
  open channel, configure) — previously any step whose app was installed
  was silently auto-ticked and running apps marked the whole goal done.
- Completion CTAs now go to the app you just set up ("Go to my shop
  (BTCPay)" etc.) instead of the generic services list; iframe apps open
  in the on-top app overlay.
- On-chain send modal gains a "Send all funds" sweep toggle, backed by
  LND's send_all on the backend (amount no longer required when sweeping).
- Demo/mock: bitcoin.getinfo now returns the block_height/sync_progress
  contract the UI reads and simulates a ~90s IBD ramp per visitor so the
  timer, toast, and fund flow can all be demoed live; channel list data
  fixed (status/channel_point/liquidity totals — the panel previously
  crashed on the missing status field); sendcoins supports send_all.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 21:51:12 -04:00
90bedc2a25 Merge pull request 'feat(ui): WireGuard remote-access onboarding + Connected Nodes cleanup' (#95) from identities-mobile-polish into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m38s
2026-07-17 01:39:02 +00:00
Dorian
a66e4bac6d fix(ui): Connected Nodes — consistent bottom actions
Drop the 'Discover Nodes on Nostr' button and the >=1800px header
buttons; Find Nodes + Refresh are now a 50/50 bottom pair on every
screen size, with Refresh acting on the active tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:38:07 +01:00
Dorian
af2dfd0bd6 chore(ui): mirror official WireGuard Android APK for the pairing flow
com.wireguard.android 1.0.20260315 (versionCode 519) from
download.wireguard.com, signer WireGuard LLC (cert sha256 84a13fa2…),
file sha256 f92971bc804f4c448e8845ae97e426095eab06ec09a2473a3fa2cfe7288e3298.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:32:02 +01:00
Dorian
68c25534d4 feat(ui): remote-access steps in the companion pairing flow
After 'I've installed it' the modal now walks WireGuard setup before
pairing: install WireGuard (hosted APK + QR), scan the node's tunnel
config (auto-provisioned 'companion-phone' peer via the existing
vpn.create-peer/peer-config RPCs, rendered client-side), switch it on,
then pair. Real nodes pair against http://10.44.0.1 — the tunnel only
routes AllowedIPs 10.44.0.0/16, so the LAN origin is unreachable
remotely. The demo fakes the tunnel via mock RPCs and keeps its public
pairing URL. REMOTE_ACCESS_STEPS=false restores the old two-screen flow.
Phones get the config as a downloadable .conf (can't scan own screen).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:32:02 +01:00
45c9def94a Merge pull request 'fix(ui): web5 mobile polish — identity profile cards, visibility card, demo avatars, title alignment' (#94) from identities-mobile-polish into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m32s
2026-07-17 01:13:56 +00:00
Dorian
299f7d8f39 fix(ui): center OpenWrt Gateway title with its back button
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:12:54 +01:00
Dorian
bb231e82b4 fix(ui): web5 cards mobile polish + demo identity avatars
- Identities render as profile cards (banner, overlapping avatar, bio)
  in narrow containers via container query; wide/desktop row untouched
- Demo identities get purpose-tinted SVG avatars (public/demo-avatars)
  in both mock identity.list/get branches
- Node Visibility: description stacks under icon+title on mobile,
  Refresh is a full-width bottom action on all sizes, discoverable
  warning line removed, and the demo no longer exposes an onion address

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:12:54 +01:00
e2309cc2ac Merge pull request 'fix(android): pairing QR now actually scannable (v0.4.14)' (#93) from companion-qr-scan-fix into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m31s
2026-07-17 01:04:08 +00:00
Dorian
a48499daeb chore(android): update companion apk download 2026-07-17 02:03:27 +01:00
Dorian
6df9afe684 fix(android): pairing QR now actually scannable
Camera decode failed because every lever was at its minimum: the web
modal generated the QR at 112px (~2.5px per module before upscaling),
the analysis stream was CameraX's 640x480 default, and ZXing ran
without TRY_HARDER. Generate the QR at 512px (displayed 192px with a
real quiet zone), analyze at 1280x720, decode with TRY_HARDER plus an
inverted-luminance fallback. v0.4.14 (versionCode 18).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:03:11 +01:00
9d21dd5111 Merge pull request 'feat(android): companion QR pairing — scan, deep link, auto-login (v0.4.13)' (#92) from companion-qr-pairing into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m34s
2026-07-17 00:46:47 +00:00
Dorian
ddbfaf1d00 chore(android): update companion apk download 2026-07-17 01:46:24 +01:00
archipelago
2e5f67a59a feat(ui): companion app banner in the App Store + APK download unblocked
Some checks failed
Demo images / Build & push demo images (push) Has been cancelled
The App Store gains a "Your Node. In Your Pocket." hero banner for the
mobile companion app, in the same format as the featured-app banner, with
the app running on a phone mockup rising out of the right edge. Install
(or clicking anywhere on it) opens the Remote Companion download/pairing
modal — now openable on demand via a shared trigger instead of only
auto-showing once.

Also fixes the companion APK download on PWA installs: /packages/ now
bypasses the service worker's SPA fallback, which was answering the
download click with index.html instead of the file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:46:08 -04:00
Dorian
51d1c89137 fix(android): center connect-screen content vertically
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 01:46:07 +01:00
Dorian
785fb3d2be chore(android): update companion apk download 2026-07-17 01:41:59 +01:00
Dorian
2b3a18f189 feat(android): QR pairing — scan, deep link, one-step auto-login
Companion side of docs/companion-pairing-qr.md (v1 contract):
- Connect screen lands on Scan Node's QR / Enter Manually; NESMenu gains
  an add-server-by-QR icon button
- QrScannerOverlay: CameraX + ZXing core (pinned, on-device, no ML Kit)
- ServerQrParser: archipelago://pair?v=1&url=..[&pw=..]; unknown major v
  -> 'update the app'; upsert by origin so re-scans never duplicate
- archipelago:// deep link registered (singleTask + onNewIntent) for the
  modal's 'Open in companion app' button
- WebView auto-fills and submits Login.vue's password form when a stored
  password exists — demo QR is one step to a logged-in session
- Web modal + handoff doc copy synced to the real label: Scan Node's QR
- v0.4.13 (versionCode 17)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 01:41:35 +01:00
archipelago
3028685e6b feat(ui): companion modal pairing screen — scan a QR to auto-connect the app
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m45s
The Remote Companion intro modal gains a second screen: an "I've installed
it" button next to the download button slides over to a pairing QR the
companion app scans to load the server details automatically, with a Back
button to return. The QR is an archipelago://pair deep link carrying the
server URL — the demo embeds https://demo.archipelago-foundation.org plus
the shared demo password so a scan lands in a logged-in demo session; real
nodes send only the address (kiosk advertises the mDNS .local name since
its own origin is localhost). Phones also get a tap-to-open deep-link
button since you can't scan your own screen.

App-side contract + requirements handed off in docs/companion-pairing-qr.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 20:07:56 -04:00
archipelago
1a3170f1c3 fix(ui): companion popup waits for sustained calm; connection banner stops crying wolf
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m36s
Two "unpolished" moments, worst on the demo:

- CompanionIntroOverlay gated on a point-in-time check of the reveal
  cinematic flag. On a cold cache the entrance video can start buffering
  after the 5s base delay, so the flag was still false when sampled and
  the popup cut into the cinematic anyway. It now shows only after the
  scene has been continuously calm for the full grace window.

- ConnectionBanner's flat 2.5s debounce fired on every tab-return and on
  first dashboard paint: a dead WebSocket is the NORMAL state right then
  (browsers kill background-tab sockets; first paint races the initial
  connect), and reconnects routinely exceed 2.5s over real links. Those
  moments now get a 10s runway (15s window after load/resume); genuine
  mid-session drops keep the 2.5s response. On the demo the blip banner
  is suppressed entirely — it runs against a local mock, so "Connection
  lost" there is pure noise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:42:57 -04:00