fix(mesh): correct share-compute copy and unknown-capacity state

Two UX-only fixes, no behavior change:

An unfetched snapshot (null) reported "No mesh capacity yet", conflating
"not checked yet" with "the community has nothing" — a verdict on everyone
else's machines rendered during startup. It now reads "Checking mesh
capacity…" and only claims an empty mesh once a snapshot actually says so.

Sharing copy described the wrong mechanism: a sharing machine contributes
inference capacity, it does not host other members' agent processes. "Turn on
to let members run agents on this computer" becomes "Share compute to run
models", and the settings card description is corrected to match.

Also plumbs two data additions the detail view needs: snapshot.memberCount
(NIP-43 roster size, the denominator for "N of M sharing") and the
routing_metrics.pressure split (locallyServed / remotelyServed /
endpointServed) — completed requests rather than attempts, which is the honest
basis for "this ran on my machine" vs "this ran on someone else's".

Signed-off-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
Michael Neale
2026-08-04 19:38:07 +10:00
parent dda20b5d62
commit d830359a23
6 changed files with 60 additions and 4 deletions
@@ -80,6 +80,13 @@ pub struct MeshSnapshot {
pub devices: Vec<MeshSnapshotDevice>,
/// True when the local member's own device is among the sharing devices.
pub includes_self: bool,
/// Members in the community right now (NIP-43 roster size).
///
/// The denominator for "N of M sharing". Deliberately NOT used to estimate
/// unshared capacity: a member who never starts a node publishes no status
/// note, so their hardware is unknown by design — they never consented to
/// disclose it. Ghost nodes in the topology are a count, never GB.
pub member_count: usize,
/// Why the snapshot is empty, when it is. Never a hard error: an empty
/// mesh is a normal, expected state.
pub reason: Option<String>,
@@ -238,6 +245,7 @@ pub fn snapshot_from_events(
};
MeshSnapshot {
member_count: members.len(),
sharing_device_count,
shared_capacity_gb,
models,
+16
View File
@@ -29,6 +29,18 @@ pub struct MeshServingUsage {
pub endpoint_attempts: u64,
/// Other nodes currently visible as peers.
pub peers: u64,
/// Completed requests this node's own GPU answered.
///
/// From `routing_metrics.pressure`. Unlike the `*_attempts` counters these
/// count finished requests rather than tries, so they are the honest basis
/// for "this ran here" vs "this ran on someone else's machine".
pub locally_served: u64,
/// Completed requests a peer answered for this node — i.e. this machine
/// CONSUMED another member's compute. The one true "I used someone else's
/// hardware" figure available today.
pub remotely_served: u64,
/// Completed requests answered by a configured endpoint (not a mesh peer).
pub endpoint_served: u64,
}
/// Pure extractor: project a raw SDK status payload into [`MeshServingUsage`].
@@ -40,6 +52,7 @@ pub fn serving_usage_from_payload(payload: &serde_json::Value) -> MeshServingUsa
let u64_at = |v: &serde_json::Value| v.as_u64().unwrap_or(0);
let rm = payload.get("routing_metrics");
let local = rm.and_then(|m| m.get("local_node"));
let pressure = rm.and_then(|m| m.get("pressure"));
let get_u64 = |obj: Option<&serde_json::Value>, key: &str| {
obj.and_then(|o| o.get(key)).map(u64_at).unwrap_or(0)
};
@@ -64,5 +77,8 @@ pub fn serving_usage_from_payload(payload: &serde_json::Value) -> MeshServingUsa
.and_then(serde_json::Value::as_array)
.map(|a| a.len() as u64)
.unwrap_or(0),
locally_served: get_u64(pressure, "locally_served_request_count"),
remotely_served: get_u64(pressure, "remotely_served_request_count"),
endpoint_served: get_u64(pressure, "endpoint_request_count"),
}
}
@@ -105,7 +105,9 @@ test("an empty mesh reads as an honest empty state", () => {
),
"No mesh capacity yet",
);
assert.equal(describeMeshCapacity(null), "No mesh capacity yet");
// `null` is "not fetched yet" and must not be reported as an empty mesh:
// that would pass judgement on everyone else's machines during startup.
assert.equal(describeMeshCapacity(null), "Checking mesh capacity…");
});
test("capacity formatting keeps small figures meaningful", () => {
@@ -76,7 +76,13 @@ function plural(n: number, one: string, many = `${one}s`): string {
* says "total": the underlying query is capped at 100 members.
*/
export function describeMeshCapacity(snapshot: MeshSnapshot | null): string {
if (!snapshot || snapshot.sharingDeviceCount === 0) {
// `null` is "not fetched yet", NOT "the community has nothing". Claiming an
// empty mesh before the first snapshot lands reads as a verdict on everyone
// else's machines while the card is still starting up.
if (!snapshot) {
return "Checking mesh capacity…";
}
if (snapshot.sharingDeviceCount === 0) {
return "No mesh capacity yet";
}
const { sharingDeviceCount: count, sharedCapacityGb: gb } = snapshot;
@@ -280,7 +286,7 @@ export function deriveMeshCardModel({
...base,
tone: "idle",
headline: capacity,
detail: ready ?? "Turn on to let members run agents on this computer.",
detail: ready ?? "Share compute to run models.",
showSoloHint: false,
};
}
@@ -227,7 +227,7 @@ export function MeshComputeSettingsCard() {
<section className="min-w-0" data-testid="settings-mesh-share-compute">
<SettingsSectionHeader
title="Share compute"
description="Share this machine with members of this relay so they can run agents here."
description="Share this machine's compute so members can run models on it."
/>
{error ? (
+24
View File
@@ -83,6 +83,22 @@ export type MeshServingUsage = {
remoteAttempts: number;
endpointAttempts: number;
peers: number;
/**
* Completed requests this machine's own GPU answered.
*
* From `routing_metrics.pressure`. Unlike the `*Attempts` counters these are
* finished requests rather than tries, which makes them the honest basis for
* "this ran here" vs "this ran on someone else's machine".
*/
locallyServed: number;
/**
* Completed requests a peer answered for this machine — i.e. this machine
* CONSUMED another member's compute. The one true "I used someone else's
* hardware" figure available today.
*/
remotelyServed: number;
/** Completed requests answered by a configured endpoint, not a mesh peer. */
endpointServed: number;
};
export async function meshServingUsage(): Promise<MeshServingUsage> {
@@ -156,6 +172,14 @@ export type MeshSnapshot = {
models: string[];
devices: MeshSnapshotDevice[];
includesSelf: boolean;
/**
* Members in the community right now (NIP-43 roster size).
*
* The denominator for "N of M sharing". Never used to estimate unshared
* capacity: a member who never starts a node publishes no status note, so
* their hardware is unknown by design. Ghosts are a count, never GB.
*/
memberCount: number;
reason: string | null;
};