mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands #2467 + #3208) (#3910)
Relands **#2467** (extract `buzz-voice` crate) and **#3208** (Pocket
voice settings) onto main, after #3266 and #3180 merged.
## Why a fresh PR
The repo is squash-only with delete-branch-on-merge. Squashing #3266
deleted `jtennant/pocket-tts-2026-04`, which was #2467's base — GitHub
auto-closed #2467 and it cannot be reopened. Squash merges also sever
ancestry, so GitHub's natural merge-base reports phantom conflicts for
the whole remaining stack.
## Content provenance
- Byte-identical to the blessed `jt/buzz-voice-refactor` branch
(`93029c577`, tree `6729e0eff` — reviewed by Dawn (#2467) and Max
(#3208) at exact heads) **except** the three files where #3180 and #3208
genuinely interact.
- Three-file resolution (union of both sides):
- `huddle/mod.rs` — #3180's pipeline re-exports + #3208's
`agent_tts_routing` imports.
- `huddle/state.rs` — `reset_preserving_generation` preserves both
`huddle_generation` (#3180) and `tts_enabled` (#3208); test sets merged
into one `tests` module.
- `desktop/src/testing/e2eBridge.ts` — both switch arms kept; no
duplicate case labels.
## Verification at cf32dacd7
- `cargo test` (desktop/src-tauri, pinned 1.95.0): **2047 + 3 pass / 0
fail** (14 ignored: 8 keychain, 4 real_relay, 2 flag-gated)
- `cargo clippy --all-targets -- -D warnings`: clean; `cargo fmt
--check`: clean
- `cargo check --workspace` (root, includes new `buzz-voice` member):
clean; `cargo test -p buzz-voice`: 5/0
- `pnpm test`: **3885 / 0**; `tsc --noEmit`: clean; lint: clean
The 3180×3208 interaction resolution is getting an independent team
re-review before merge.
Buzz channel: buzz-desktop-voice `fd5fb402-b651-4238-89b1-bb3e2fa4dc96`,
thread `b4798ecc`.
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
This commit is contained in:
co-authored by
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d
parent
689617af7a
commit
61ba9dfaa0
@@ -12,7 +12,7 @@ import {
|
||||
AUTH_TIMEOUT_MS,
|
||||
HISTORY_TIMEOUT_MS,
|
||||
PUBLISH_TIMEOUT_MS,
|
||||
} from "@/shared/api/relayClientSession";
|
||||
} from "@/shared/api/relayClientTimings";
|
||||
|
||||
type PendingHistory = {
|
||||
events: RelayEvent[];
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
buildChannelAuxFilter,
|
||||
buildChannelReactionAuxFilter,
|
||||
buildChannelStructuralAuxFilter,
|
||||
buildHuddleTtsLiveFilter,
|
||||
} from "./relayChannelFilters.ts";
|
||||
|
||||
const CHANNEL = "36411e44-0e2d-4cfe-bd6e-567eb169db9f";
|
||||
@@ -14,6 +15,14 @@ const IDS = [
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
];
|
||||
|
||||
test("huddle TTS filter is future-only for both message kinds", () => {
|
||||
assert.deepEqual(buildHuddleTtsLiveFilter(CHANNEL), {
|
||||
kinds: [9, 40002],
|
||||
"#h": [CHANNEL],
|
||||
limit: 0,
|
||||
});
|
||||
});
|
||||
|
||||
// Regression: reaction (kind:7) and reaction-removal (kind:5) events carry only
|
||||
// an `e` tag, no channel `h` tag. An `#h`-scoped aux query never matches them,
|
||||
// so removed historical reactions reappear. The aux filters must key on `#e`
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
KIND_DELETION,
|
||||
KIND_NIP29_DELETE_EVENT,
|
||||
KIND_REACTION,
|
||||
KIND_STREAM_MESSAGE,
|
||||
KIND_STREAM_MESSAGE_V2,
|
||||
KIND_STREAM_MESSAGE_EDIT,
|
||||
} from "@/shared/constants/kinds";
|
||||
import type { RelaySubscriptionFilter } from "@/shared/api/relayClientShared";
|
||||
@@ -40,6 +42,17 @@ export function buildChannelFilter(
|
||||
return filter;
|
||||
}
|
||||
|
||||
/** Strictly live huddle message filter: zero stored rows, future messages only. */
|
||||
export function buildHuddleTtsLiveFilter(
|
||||
channelId: string,
|
||||
): RelaySubscriptionFilter {
|
||||
return {
|
||||
kinds: [KIND_STREAM_MESSAGE, KIND_STREAM_MESSAGE_V2],
|
||||
"#h": [channelId],
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* History filter for cold-load and scrollback: message kinds *only*, so the
|
||||
* `limit` budget buys visible message depth. Auxiliary events (reactions,
|
||||
|
||||
@@ -53,20 +53,19 @@ import {
|
||||
} from "@/shared/api/relayReconnectPolicy";
|
||||
import { RelayReconnectWaiters } from "@/shared/api/relayReconnectWaiters";
|
||||
import { RelayStallWatchdog } from "@/shared/api/relayStallWatchdog";
|
||||
import {
|
||||
AUTH_TIMEOUT_MS,
|
||||
BACKOFF_RESET_STABLE_MS,
|
||||
EVENT_BATCH_MS,
|
||||
HISTORY_TIMEOUT_MS,
|
||||
PUBLISH_TIMEOUT_MS,
|
||||
RECONNECT_BASE_DELAY_MS,
|
||||
RECONNECT_MAX_DELAY_MS,
|
||||
STALL_CHECK_INTERVAL_MS,
|
||||
STALL_IDLE_TIMEOUT_MS,
|
||||
} from "@/shared/api/relayClientTimings";
|
||||
import { closeWebSocket } from "@/shared/api/relayWebSocketClose";
|
||||
import { buildThreadReferenceTags } from "@/features/messages/lib/threading";
|
||||
const RECONNECT_BASE_DELAY_MS = 1_000,
|
||||
RECONNECT_MAX_DELAY_MS = 30_000,
|
||||
EVENT_BATCH_MS = 16;
|
||||
|
||||
export const AUTH_TIMEOUT_MS = 25_000;
|
||||
export const HISTORY_TIMEOUT_MS = 25_000;
|
||||
export const PUBLISH_TIMEOUT_MS = 25_000;
|
||||
|
||||
export const BACKOFF_RESET_STABLE_MS = 60_000;
|
||||
|
||||
const STALL_CHECK_INTERVAL_MS = 10_000;
|
||||
const STALL_IDLE_TIMEOUT_MS = 60_000;
|
||||
|
||||
export class RelayClient {
|
||||
private wsId: number | null = null;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export const RECONNECT_BASE_DELAY_MS = 1_000;
|
||||
export const RECONNECT_MAX_DELAY_MS = 30_000;
|
||||
export const EVENT_BATCH_MS = 16;
|
||||
|
||||
/**
|
||||
* Op-level timeouts tolerate degraded networks where TLS handshakes and DNS
|
||||
* resolution can take several seconds.
|
||||
*/
|
||||
export const AUTH_TIMEOUT_MS = 25_000;
|
||||
export const HISTORY_TIMEOUT_MS = 25_000;
|
||||
export const PUBLISH_TIMEOUT_MS = 25_000;
|
||||
|
||||
/**
|
||||
* A stability-gated reset prevents reconnect flapping from erasing backoff.
|
||||
*/
|
||||
export const BACKOFF_RESET_STABLE_MS = 60_000;
|
||||
|
||||
/** Passive liveness thresholds for the relay heartbeat stream. */
|
||||
export const STALL_CHECK_INTERVAL_MS = 10_000;
|
||||
export const STALL_IDLE_TIMEOUT_MS = 60_000;
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
buildReconnectReplayFilter,
|
||||
replayLiveSubscriptions,
|
||||
REPLAY_BATCH_SIZE,
|
||||
shouldPageReconnectReplay,
|
||||
} from "./relayReconnectReplay.ts";
|
||||
import { buildChannelFilter } from "./relayChannelFilters.ts";
|
||||
|
||||
@@ -113,6 +114,31 @@ test("reconnect replay caps large steady-state limits", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("reconnect replay preserves the live-only zero-history contract", () => {
|
||||
const filter = {
|
||||
kinds: [9],
|
||||
"#h": ["channel-1"],
|
||||
limit: 0,
|
||||
};
|
||||
|
||||
assert.deepEqual(replayFilter(filter, 123), {
|
||||
kinds: [9],
|
||||
"#h": ["channel-1"],
|
||||
limit: 0,
|
||||
since: 123,
|
||||
});
|
||||
});
|
||||
|
||||
test("live-only subscriptions do not page reconnect history", () => {
|
||||
const filter = {
|
||||
kinds: [9],
|
||||
"#h": ["channel-1"],
|
||||
limit: 0,
|
||||
};
|
||||
|
||||
assert.equal(shouldPageReconnectReplay(filter), false);
|
||||
});
|
||||
|
||||
test("reconnect replay keeps the stricter existing since window", () => {
|
||||
const filter = {
|
||||
kinds: [9],
|
||||
|
||||
Reference in New Issue
Block a user