fix(desktop): release foreground work after reconnect authentication

Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
This commit is contained in:
Brain
2026-09-21 06:49:35 -06:00
parent 564a7da84e
commit 326bdcbd57
6 changed files with 770 additions and 29 deletions

View File

@@ -0,0 +1,439 @@
import assert from "node:assert/strict";
import test from "node:test";
const timers = new Map();
let nextTimer = 0;
let invoke;
globalThis.window = {
setTimeout: (fn, ms) => {
timers.set(++nextTimer, { fn, ms });
return nextTimer;
},
clearTimeout: (id) => timers.delete(id),
setInterval: (fn, ms) => window.setTimeout(fn, ms),
clearInterval: (id) => timers.delete(id),
__TAURI_INTERNALS__: {
transformCallback: () => ++nextTimer,
unregisterCallback: () => {},
invoke: (...args) => invoke(...args),
},
};
const { RelayClient } = await import("./relayClientSession.ts");
const { resetRateLimitGate } = await import("./relayRateLimitGate.ts");
function deferred() {
let resolve;
let reject;
const promise = new Promise((yes, no) => {
resolve = yes;
reject = no;
});
return { promise, resolve, reject };
}
async function flush() {
for (let i = 0; i < 100; i++) await Promise.resolve();
}
function row(id) {
return {
id: id.repeat(64),
pubkey: "a".repeat(64),
kind: 9,
created_at: Math.floor(Date.now() / 1000),
tags: [["h", "background"]],
content: id,
sig: "b".repeat(128),
};
}
function fixture(t) {
timers.clear();
resetRateLimitGate();
const client = new RelayClient();
const sockets = [];
const frames = [];
const repairs = [];
let authHeld = false;
let failReplay = null;
let failNextOperation = false;
invoke = async (command, args) => {
if (command === "get_relay_ws_url") return "wss://fixture.invalid";
if (command === "sign_event") return JSON.stringify(row("3"));
if (command === "create_auth_event") return JSON.stringify({ id: "auth" });
if (command === "plugin:websocket|connect") {
sockets.push(args.onMessage);
args.onMessage.onmessage({ type: "Text", data: '["AUTH","challenge"]' });
return sockets.length;
}
if (command === "plugin:websocket|disconnect") return;
if (command === "plugin:websocket|send") {
const frame = JSON.parse(args.message.data);
frames.push({ socket: args.id, frame });
if (failNextOperation && (frame[0] === "REQ" || frame[0] === "EVENT")) {
failNextOperation = false;
authHeld = true;
throw new Error("fixture initial send failed");
}
if (frame[0] === "EVENT") deliver(args.id, ["OK", frame[1].id, true, ""]);
if (frame[0] === "AUTH" && !authHeld)
deliver(args.id, ["OK", "auth", true, ""]);
if (frame[0] === "REQ") {
if (failReplay && frame[2]["#h"]?.[0] === "background")
return failReplay.promise;
deliver(args.id, ["EOSE", frame[1]]);
}
return;
}
if (command === "get_channel_reconnect_repair") {
const gate = deferred();
repairs.push({ args, ...gate });
return gate.promise;
}
assert.fail(`Unexpected native call: ${command}`);
};
function deliver(socket, frame) {
sockets[socket - 1].onmessage({
type: "Text",
data: JSON.stringify(frame),
});
}
t.after(async () => {
for (const repair of repairs) repair.resolve([]);
failReplay?.resolve();
await flush();
client.disconnect();
resetRateLimitGate();
});
return {
client,
sockets,
frames,
repairs,
deliver,
failNextOperation: () => {
failNextOperation = true;
},
holdAuth: () => {
authHeld = true;
},
failReplay: (gate) => {
failReplay = gate;
},
};
}
async function seedAndDisconnect(f) {
const delivered = [];
await f.client.subscribeToChannelLive("background", (event) =>
delivered.push(event.id),
);
const sub = f.frames.find(({ frame }) => frame[0] === "REQ").frame[1];
f.deliver(1, ["EVENT", sub, row("1")]);
f.deliver(1, ["EOSE", sub]);
await flush();
assert.deepEqual(delivered, [row("1").id]);
f.sockets[0].onmessage({
type: "Close",
data: { code: 1006, reason: "fixture drop" },
});
await flush();
return delivered;
}
for (const beforeAuth of [false, true]) {
test(`foreground subscription does not wait for background repair (${beforeAuth ? "queued before auth" : "after auth"})`, async (t) => {
const f = fixture(t);
const delivered = await seedAndDisconnect(f);
let completed = 0;
f.client.subscribeToReconnects(() => completed++);
if (beforeAuth) f.holdAuth();
const reconnect = f.client.resumeReconnect();
await flush();
let ready = false;
const foreground = f.client
.subscribeToChannelLive("foreground", () => {})
.then((dispose) => {
ready = true;
return dispose;
});
await flush();
if (beforeAuth) {
assert.equal(ready, false, "authentication remains mandatory");
assert.equal(
f.frames.some(
({ socket, frame }) => socket === 2 && frame[0] === "REQ",
),
false,
);
f.deliver(2, ["OK", "auth", true, ""]);
await flush();
}
assert.equal(f.repairs.length, 1, "real native repair must be in flight");
assert.equal(completed, 0, "reconnect completion still waits for repair");
try {
assert.equal(
ready,
true,
"authenticated foreground REQ must not wait for unrelated HTTP repair",
);
} finally {
f.repairs[0].resolve([row("2")]);
await reconnect;
const dispose = await foreground;
await flush();
await dispose();
}
assert.deepEqual(
delivered,
[row("1").id, row("2").id],
"missed history still arrives",
);
assert.equal(completed, 1);
});
}
test("late repair completion cannot notify or deliver into a replacement community", async (t) => {
const f = fixture(t);
const delivered = await seedAndDisconnect(f);
await f.client.resumeReconnect();
await flush();
assert.equal(f.repairs.length, 1);
f.client.disconnect();
await f.client.preconnect();
await flush();
let notified = 0;
f.client.subscribeToReconnects(() => notified++);
const socket = f.client.wsId;
f.repairs[0].resolve([row("2")]);
await flush();
assert.equal(f.client.wsId, socket);
assert.equal(f.client.getConnectionState(), "connected");
assert.equal(notified, 0);
assert.deepEqual(delivered, [row("1").id]);
});
test("current replay transport failure resets the socket and schedules recovery", async (t) => {
const f = fixture(t);
await seedAndDisconnect(f);
const send = deferred();
f.failReplay(send);
await f.client.resumeReconnect();
await flush();
assert.equal(f.client.getConnectionState(), "connected");
send.reject(new Error("fixture send failed"));
await flush();
assert.equal(f.client.wsId, null);
assert.equal(f.client.getConnectionState(), "reconnecting");
assert.notEqual(f.client.reconnectTimeout, null);
});
test("stale replay transport failure cannot reset a replacement socket", async (t) => {
const f = fixture(t);
await seedAndDisconnect(f);
const send = deferred();
f.failReplay(send);
await f.client.resumeReconnect();
await flush();
f.client.disconnect();
f.failReplay(null);
await f.client.preconnect();
await flush();
const socket = f.client.wsId;
send.reject(new Error("late fixture send failure"));
await flush();
assert.equal(f.client.wsId, socket);
assert.equal(f.client.getConnectionState(), "connected");
assert.equal(f.client.reconnectTimeout, null);
});
test("rejected authentication never releases a waiting foreground request", async (t) => {
const f = fixture(t);
f.holdAuth();
const connecting = f.client.preconnect();
const rejected = assert.rejects(connecting, /restricted/);
await flush();
const foreground = f.client.subscribeToChannelLive("foreground", () => {});
const foregroundRejected = assert.rejects(foreground, /restricted/);
f.deliver(1, ["OK", "auth", false, "restricted: fixture denial"]);
await Promise.all([rejected, foregroundRejected]);
assert.equal(
f.frames.some(({ frame }) => frame[0] === "REQ"),
false,
);
assert.equal(f.client.wsId, null);
assert.equal(f.client.getConnectionState(), "disconnected");
});
function expireCooldown() {
const entry = [...timers.entries()].find(([, timer]) => timer.ms === 10_000);
assert.ok(entry, "expected WS admission timer");
timers.delete(entry[0]);
entry[1].fn();
}
for (const switchCommunity of [false, true]) {
test(`foreground admission preserves WS cooldown (switch=${switchCommunity})`, async (t) => {
const f = fixture(t);
await seedAndDisconnect(f);
await f.client.resumeReconnect();
f.deliver(2, ["NOTICE", "rate-limited: retry in 10s"]);
await flush();
const foreground = f.client.subscribeToChannelLive("foreground", () => {});
const outcome = foreground.then(
() => "ready",
() => "superseded",
);
await flush();
assert.equal(
f.frames.some(
({ frame }) =>
frame[0] === "REQ" && frame[2]["#h"]?.[0] === "foreground",
),
false,
);
if (switchCommunity) {
f.client.disconnect();
resetRateLimitGate();
await f.client.preconnect();
assert.equal(await outcome, "superseded");
assert.equal(f.client.subscriptions.size, 0);
assert.equal(f.client.getConnectionState(), "connected");
} else {
expireCooldown();
await flush();
assert.equal(await outcome, "ready");
assert.equal(f.repairs.length, 1, "HTTP repair stays held");
}
});
}
for (const operation of ["subscribe", "publish"]) {
for (const switchCommunity of [false, true]) {
test(`${operation} retry respects cooldown armed during reconnect (switch=${switchCommunity})`, async (t) => {
const f = fixture(t);
await f.client.preconnect();
f.failNextOperation();
const pending =
operation === "subscribe"
? f.client.subscribeToChannelLive("foreground", () => {})
: f.client.publishEvent(row("3"), "timeout", "send failed");
const outcome = pending.then(
() => "ready",
() => "superseded",
);
await flush();
const reconnect = f.client.resumeReconnect();
await flush();
f.deliver(2, ["NOTICE", "rate-limited: retry in 10s"]);
f.deliver(2, ["OK", "auth", true, ""]);
await reconnect;
await flush();
assert.equal(
f.frames.some(
({ socket, frame }) =>
socket === 2 && ["REQ", "EVENT"].includes(frame[0]),
),
false,
);
if (switchCommunity) {
f.client.disconnect();
resetRateLimitGate();
const replacement = f.client.preconnect();
await flush();
f.deliver(3, ["OK", "auth", true, ""]);
await replacement;
assert.equal(await outcome, "superseded");
assert.equal(
f.frames.some(
({ socket, frame }) =>
socket === 3 && ["REQ", "EVENT"].includes(frame[0]),
),
false,
);
assert.equal(f.client.getConnectionState(), "connected");
} else {
expireCooldown();
await flush();
assert.equal(await outcome, "ready");
}
});
}
}
test("queued old repair workers cannot re-pin floors after same-community successor completes", async (t) => {
const f = fixture(t);
for (let i = 0; i < 6; i++) {
await f.client.subscribeToChannelLive(`background-${i}`, () => {});
const sub = f.frames.at(-1).frame[1];
f.deliver(1, [
"EVENT",
sub,
{ ...row("1"), tags: [["h", `background-${i}`]] },
]);
f.deliver(1, ["EOSE", sub]);
}
await flush();
const close = (socket) =>
f.sockets[socket - 1].onmessage({
type: "Close",
data: { code: 1006, reason: "drop" },
});
close(1);
await flush();
await f.client.resumeReconnect();
await flush();
assert.equal(f.repairs.length, 4);
close(2);
await flush();
await f.client.resumeReconnect();
await flush();
assert.equal(f.repairs.length, 8);
for (const repair of f.repairs.slice(4)) repair.resolve([]);
await flush();
assert.equal(f.repairs.length, 10);
for (const repair of f.repairs.slice(8)) repair.resolve([]);
await flush();
assert.ok(
[...f.client.subscriptions.values()].every(
(sub) => sub.pendingReplaySince === undefined,
),
);
for (const repair of f.repairs.slice(0, 4)) repair.resolve([row("2")]);
await flush();
assert.ok(
[...f.client.subscriptions.values()].every(
(sub) => sub.pendingReplaySince === undefined,
),
"stale queued workers must not mutate completed successor repair",
);
assert.equal(f.repairs.length, 10, "old queued repair stays inert");
});
test("finite read and accepted send proceed while unrelated recovery is held", async (t) => {
const f = fixture(t);
await seedAndDisconnect(f);
f.holdAuth();
const reconnect = f.client.resumeReconnect();
await flush();
let readDone = false;
let sendDone = false;
const read = f.client.fetchEvents({ kinds: [1] }).then(() => {
readDone = true;
});
const send = f.client.sendMessage("foreground", "hello").then(() => {
sendDone = true;
});
await flush();
assert.equal(readDone || sendDone, false);
f.deliver(2, ["OK", "auth", true, ""]);
await flush();
assert.equal(f.repairs.length, 1);
try {
assert.equal(readDone, true, "EOSE completes finite read before repair");
assert.equal(sendDone, true, "OK acknowledges send before repair");
} finally {
f.repairs[0].resolve([]);
await Promise.all([reconnect, read, send]);
}
});

View File

@@ -38,7 +38,10 @@ import {
import { getChannelReconnectRepairEvents } from "@/shared/api/channelReconnectRepair";
import { replayLiveSubscriptions } from "@/shared/api/relayReconnectReplay";
import { publishSessionEvent } from "@/shared/api/relayEventPublisher";
import { activateRateLimitIfSignalled } from "@/shared/api/relayRateLimitGate";
import {
activateRateLimitIfSignalled,
waitForRateLimit,
} from "@/shared/api/relayRateLimitGate";
import {
fetchChunkedHistory,
requestFirstEventGated,
@@ -562,15 +565,34 @@ export class RelayClient {
await Promise.race([drain, authentication, inbound.overflow]);
await drain;
await authentication;
if (generation !== this.connectionGeneration || this.wsId !== wsId) {
throw new Error("Relay authentication was superseded.");
}
this.stabilityTimer = window.setTimeout(() => {
this.stabilityTimer = null;
this.reconnectDelayMs = RECONNECT_BASE_DELAY_MS;
}, BACKOFF_RESET_STABLE_MS);
this.connectionStateEmitter.set("connected");
await this.replayLiveSubscriptions();
this.stallWatchdog.start();
this.emitReconnectIfNeeded();
// Authentication makes the socket usable. Unrelated channel backfill
// must not hold new subscriptions, reads, or writes behind connectPromise.
// Reconnect notifications still mean the recovery pass has finished.
void this.replayLiveSubscriptions().then(
() => {
if (generation !== this.connectionGeneration) return;
this.stallWatchdog.start();
this.emitReconnectIfNeeded();
},
(error) => {
if (generation !== this.connectionGeneration) return;
this.resetConnection(
this.normalizeRelayError(
error,
"Failed to restore relay subscriptions.",
),
);
},
);
return generation;
} catch (error) {
const connectionError = this.normalizeRelayError(
@@ -590,7 +612,15 @@ export class RelayClient {
onReady?: (readiness: LiveSubscriptionReadiness) => void,
readinessTimeoutMs = 250,
) {
await this.ensureConnected();
const ownership = this.sessionEpoch;
const generation = await this.ensureConnected();
await waitForRateLimit();
if (
ownership !== this.sessionEpoch ||
generation !== this.connectionGeneration
) {
throw new Error("Relay subscription was superseded by a session change.");
}
const subId = `live-${crypto.randomUUID()}`;
let resolveReady = (_readiness: LiveSubscriptionReadiness) => {};
@@ -679,21 +709,38 @@ export class RelayClient {
payload: unknown[],
fallbackMessage: string,
) {
const ownership = this.sessionEpoch;
let generation = this.connectionGeneration;
try {
await this.sendRaw(payload);
await this.sendRawForGeneration(payload, generation);
} catch (error) {
if (
ownership !== this.sessionEpoch ||
generation !== this.connectionGeneration
) {
throw this.normalizeRelayError(error, fallbackMessage);
}
const normalizedError = this.recoverFromSocketFailure(
error,
fallbackMessage,
);
try {
await this.ensureConnected();
await this.sendRaw(payload);
generation = await this.ensureConnected();
await waitForRateLimit();
if (
ownership !== this.sessionEpoch ||
generation !== this.connectionGeneration
) {
throw new Error(
"Relay subscription was superseded by a session change.",
);
}
await this.sendRawForGeneration(payload, generation);
} catch (retryError) {
throw this.recoverFromSocketFailure(
retryError,
normalizedError.message,
);
throw ownership === this.sessionEpoch &&
generation === this.connectionGeneration
? this.recoverFromSocketFailure(retryError, normalizedError.message)
: this.normalizeRelayError(retryError, normalizedError.message);
}
}
}
@@ -908,23 +955,14 @@ export class RelayClient {
private async replayLiveSubscriptions() {
const generation = this.connectionGeneration;
try {
await replayLiveSubscriptions({
subscriptions: this.subscriptions,
sendRaw: (payload) => this.sendRaw(payload),
requestRepair: getChannelReconnectRepairEvents,
generation,
visibleChannelId: this.visibleChannelId,
isActive: () => this.connectionGeneration === generation,
});
} catch (error) {
const reconnectError =
error instanceof Error
? error
: new Error("Failed to restore relay subscriptions.");
this.resetConnection(reconnectError);
throw reconnectError;
}
await replayLiveSubscriptions({
subscriptions: this.subscriptions,
sendRaw: (payload) => this.sendRawForGeneration(payload, generation),
requestRepair: getChannelReconnectRepairEvents,
generation,
visibleChannelId: this.visibleChannelId,
isActive: () => this.connectionGeneration === generation,
});
}
private scheduleReconnect() {

View File

@@ -56,6 +56,7 @@ export async function publishSessionEvent(
try {
retryGeneration = await session.reconnect();
await waitForRateLimit();
if (
publishOwnership !== session.ownership() ||
session.generation() !== retryGeneration ||

View File

@@ -373,6 +373,8 @@ export async function replayLiveSubscriptions({
),
pageReplayConcurrency,
async ({ subId, subscription, channelId, replaySince }) => {
// Queued workers can start after a successor has repaired this same entry.
if (!isActive() || subscriptions.get(subId) !== subscription) return;
// Backfill is best-effort: a failure here (typically a `rate-limited:`
// CLOSED on a history REQ) must never escape to the session and tear
// down the healthy, authenticated socket carrying the live REQs — that

View File

@@ -826,3 +826,178 @@ test("reconnect shares overlapping native repair pages and recovers offline mess
});
expect(reads[1].beforeId).toBeTruthy();
});
test("warm channel freshness is not blocked by another channel's reconnect repair", async ({
page,
}) => {
const start = new Date("2026-09-21T12:00:00Z");
await page.clock.setFixedTime(start);
await page.goto("/");
await page.getByTestId("channel-random").click();
await expect(page.getByTestId("chat-title")).toHaveText("random");
await expect(page.getByTestId("message-input")).toBeVisible();
// Warm the actual query; the subsequent visit uses its cached head and the
// post-subscribe authoritative refresh, not a cold independent HTTP query.
await expect
.poll(() =>
page.evaluate(() => {
const state = window.__BUZZ_E2E_QUERY_CLIENT__?.getQueryState([
"channel-messages",
"9dae0116-799b-5071-a0a8-fdd30a91a35d",
]);
return state?.status === "success" && state.fetchStatus === "idle";
}),
)
.toBe(true);
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
await emitMockMessages(page, [
{
content: "foreground availability cursor",
createdAt: start.getTime() / 1000,
},
]);
await expect(page.getByTestId("message-timeline")).toContainText(
"foreground availability cursor",
);
await page.evaluate(() => {
const w = window as unknown as {
__TAURI_INTERNALS__: {
invoke: (
command: string,
args?: unknown,
options?: unknown,
) => Promise<unknown>;
};
recoveryGate: {
started: number;
finished: number;
foregroundReqs: number;
windows: number;
release: () => void;
};
};
const original = w.__TAURI_INTERNALS__.invoke.bind(w.__TAURI_INTERNALS__);
let release = () => {};
const gate = new Promise<void>((resolve) => {
release = resolve;
});
w.recoveryGate = {
started: 0,
finished: 0,
foregroundReqs: 0,
windows: 0,
release,
};
w.__TAURI_INTERNALS__.invoke = async (command, args, options) => {
if (command === "plugin:websocket|send") {
const frame = JSON.parse(
(args as { message: { data: string } }).message.data,
);
if (
frame[0] === "REQ" &&
frame[2]?.["#h"]?.includes("9dae0116-799b-5071-a0a8-fdd30a91a35d") &&
frame[2]?.kinds?.includes(39005)
) {
w.recoveryGate.foregroundReqs++;
}
}
if (
command === "get_channel_window" &&
w.recoveryGate.foregroundReqs > 0 &&
(args as { channelId?: string }).channelId ===
"9dae0116-799b-5071-a0a8-fdd30a91a35d"
) {
w.recoveryGate.windows++;
}
if (command !== "get_channel_reconnect_repair")
return original(command, args, options);
w.recoveryGate.started++;
await gate;
const result = await original(command, args, options);
w.recoveryGate.finished++;
return result;
};
});
await setMockWebsocketUnavailable(page, true);
await disconnectMockWebsockets(page);
const missed = "fresh random head during unrelated recovery";
await page.evaluate(
({ content, createdAt }) => {
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
channelName: "random",
content,
createdAt,
});
},
{ content: missed, createdAt: start.getTime() / 1000 + 1 },
);
await page.clock.setFixedTime(new Date(start.getTime() + 10_000));
await setMockWebsocketUnavailable(page, false);
await expect
.poll(() =>
page.evaluate(
() =>
(window as unknown as { recoveryGate: { started: number } })
.recoveryGate.started,
),
)
.toBeGreaterThan(0);
await page.evaluate(() => {
const gate = (
window as unknown as {
recoveryGate: { foregroundReqs: number; windows: number };
}
).recoveryGate;
gate.foregroundReqs = 0;
gate.windows = 0;
});
try {
await page.getByTestId("channel-random").click();
await expect(page.getByTestId("chat-title")).toHaveText("random");
await expect
.poll(() =>
page.evaluate(
() =>
(window as unknown as { recoveryGate: { foregroundReqs: number } })
.recoveryGate.foregroundReqs,
),
)
.toBeGreaterThan(0);
await expect
.poll(() =>
page.evaluate(
() =>
(window as unknown as { recoveryGate: { windows: number } })
.recoveryGate.windows,
),
)
.toBeGreaterThan(0);
await expect(page.getByTestId("message-timeline")).toContainText(missed);
expect(
await page.evaluate(
() =>
(window as unknown as { recoveryGate: { finished: number } })
.recoveryGate.finished,
),
).toBe(0);
} finally {
await page.evaluate(() =>
(
window as unknown as { recoveryGate: { release: () => void } }
).recoveryGate.release(),
);
}
await expect
.poll(() =>
page.evaluate(() => {
const gate = (
window as unknown as {
recoveryGate: { started: number; finished: number };
}
).recoveryGate;
return gate.finished > 0 && gate.finished === gate.started;
}),
)
.toBe(true);
});

View File

@@ -0,0 +1,86 @@
# Authenticated foreground availability during reconnect
## Change
Base: `564a7da84e7d7096649bc22126ca86354fa81437`.
`RelayClient.ensureConnected()` previously kept its shared promise pending until
all retained live subscriptions and their native HTTP history repairs finished.
Even after successful socket AUTH, new live subscriptions, finite WS reads and
plain-text sends inherited that unrelated wait.
Keep authentication as the availability boundary, but run recovery as an
observed generation-fenced continuation. Retain history repair, visible-first
replay ordering, batching, readiness timers, server cooldowns and post-recovery
reconnect callbacks. New subscription admission and reconnect send retries check
the existing WS cooldown, then revalidate session ownership/generation. Queued
repair workers must be current before mutating repair floors.
Production scope: three files, +70/-29 lines. No new scheduler, transport,
loading-state changes or timeout reductions.
## Controlled browser evidence
A production E2E bundle with the existing mock native bridge:
1. Warm random, return to general, deliver a general event to establish a cursor.
2. Disconnect; inject an offline random message; hold native reconnect repair.
3. Reconnect successfully, wait until repair is in flight, then navigate to random.
4. Observe a **new foreground REQ containing kind 39005**, not background cache
delivery, and a subsequent random channel-window request while repair is held.
5. Release repair and require all held repair requests to finish.
The strict regression fails on the original bundle (no foreground REQ while
repair is held), passes the candidate, and is registered in the existing smoke
`relay-reconnect.spec.ts`. Merely asserting visible message content was rejected:
background subscriptions can merge that content into the cache without this fix.
Window requests before the foreground REQ are also excluded from the assertion.
Two controlled timing runs each, with repair released five seconds after the
measurement starts immediately before navigation:
| Bundle | New foreground REQ | First window request after that REQ |
| --- | ---: | ---: |
| Original, run 1 | 5,004ms | 5,006ms |
| Original, run 2 | 5,003ms | 5,005ms |
| Candidate, run 1 | 41ms | 41ms |
| Candidate, run 2 | 40ms | 40ms |
These timings prove removal of the injected dependency, **not measured savings
in the live native app**. They are not a benchmark of total channel rendering.
## Correctness gates
- 14 new tests bind the real RelayClient connection owner, AUTH and native repair
seam: before/after AUTH availability, finite-read EOSE and accepted-send OK,
rejected AUTH, current/stale replay failures, community replacement, cooldown
admission and retry ownership, and queued six-subscription recovery overlap.
- Removing each new subscription gate, retry gate, publisher retry gate or queued
worker fence in an isolated source copy makes its regression fail. Clean control
and restored control pass; the candidate working tree was not mutated.
- Full desktop suite: **6,545 passing** through the standard pre-push test lane.
- All **18 reconnect browser tests** pass, including dense missed-history replay
and shared repair reads; the final stricter window assertion also passes in
standard smoke discovery and fails the original bundle.
- Desktop check, TypeScript, production artifact matrix and file-size check pass.
- Independent read-only lifecycle review found two issues in the first slice;
cooldown admission and queued-worker ownership were fixed and re-reviewed.
Run the regression with the repository Hermit environment activated:
```sh
cd desktop
node --import ./test-loader.mjs --experimental-strip-types --test \
src/shared/api/relayClientRecoveryAvailability.test.mjs
pnpm test:e2e:smoke relay-reconnect.spec.ts
```
## Boundaries
- Fresh cold startup has no retained subscriptions; its native freeze is not
explained by this mechanism.
- Initial channel windows and thread pages use native HTTP directly. Their
latency is not removed here; the request-size cliff is a separate lead.
- Existing-channel refresh listeners still run after recovery, intentionally.
- Real relay/native UI timing and repository CI remain separate validation gates.
- No live cache edits, deployment or relay changes are part of this patch.