From e9ae377e1b543a1a74072003f6e7a6be0b854e05 Mon Sep 17 00:00:00 2001 From: Believethehype <1097224+believethehype@users.noreply.github.com> Date: Thu, 10 Sep 2026 12:52:23 +0200 Subject: [PATCH] docs: record uniform-strict offline semantics for DVM liveness --- .../plans/2026-09-10-dvm-heartbeat-liveness-plan.md | 2 +- amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/amethyst/plans/2026-09-10-dvm-heartbeat-liveness-plan.md b/amethyst/plans/2026-09-10-dvm-heartbeat-liveness-plan.md index a395a5ca72..dfe5996c2d 100644 --- a/amethyst/plans/2026-09-10-dvm-heartbeat-liveness-plan.md +++ b/amethyst/plans/2026-09-10-dvm-heartbeat-liveness-plan.md @@ -943,7 +943,7 @@ RefresheableBox(onRefresh = onRefresh) { } ``` -Notes: `heartbeatFresh?.value == false` deliberately shows the banner only when a note object exists and is stale — an unresolved heartbeat note (surfaces that never subscribed) shows nothing rather than a false "offline". `AppDefinitionEvent` is already imported in this file; add `androidx.compose.foundation.layout.Box`, `androidx.compose.foundation.layout.fillMaxSize`, `androidx.compose.ui.Alignment`, `com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.DvmOfflineBanner` (same package — no import needed) and `rememberDvmHeartbeatFresh` (same package — no import needed). +Notes: `heartbeatFresh?.value == false` exists only because `appDef` itself may be null. Per human ruling (uniform-strict), unresolved heartbeat beats count as offline on every surface — chips, home banners, and the detail screen all show the offline indicator once the beat is unresolved/stale, rather than showing nothing. `AppDefinitionEvent` is already imported in this file; add `androidx.compose.foundation.layout.Box`, `androidx.compose.foundation.layout.fillMaxSize`, `androidx.compose.ui.Alignment`, `com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.DvmOfflineBanner` (same package — no import needed) and `rememberDvmHeartbeatFresh` (same package — no import needed). - [ ] **Step 5: Manage screen rows** diff --git a/amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md b/amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md index 4ab76d7fab..e291daa14e 100644 --- a/amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md +++ b/amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md @@ -73,9 +73,13 @@ Small helper file in `amethyst/.../dvms/`: - `LocalCache.dvmHeartbeatOf(appDef: AppDefinitionEvent): DvmHeartbeatEvent?` — address lookup `Address(DvmHeartbeatEvent.KIND, appDef.pubKey, appDef.dTag())` - `DvmHeartbeatEvent.isFreshAt(now: Long): Boolean` — `createdAt >= now - 420` -- `@Composable fun rememberDvmHeartbeat(address: Address): State` — - composable-scoped subscription (§5) + staleness re-check tick (§6), shared by every - surface that renders liveness. +- `@Composable fun rememberDvmHeartbeatFresh(address: Address, accountViewModel: AccountViewModel): State` — + as built (uniform-strict ruling): returns true while the DVM has a heartbeat at most 420s + old; an unresolved/absent beat counts as offline (`false`) on every surface. The returned + `State` identity is stable for the lifetime of the call site (one unconditional + `rememberUpdatedState`), so callers may capture it across recompositions. Composable-scoped + subscription (§5) + staleness re-check tick (§6), shared by every surface that renders + liveness. ## 5. Subscriptions @@ -107,7 +111,7 @@ the session-scoped watcher for pinned DVMs. Traffic is negligible (a few pinned (alongside the existing `scope.launch { flows.collect { … } }` observers) calls `discoverDVMs.invalidateData()` every minute. The rebuild is a cheap scan (≤ a few hundred 31990s) and `refreshSuspended()` no-ops when the list is unchanged. Composables - using `rememberDvmHeartbeat` tick on a 30s cadence internally. + using `rememberDvmHeartbeatFresh` tick on a 30s cadence internally. ## 7. UI surfaces