mirror of
https://github.com/block/buzz.git
synced 2026-11-08 18:31:56 +00:00
Part of #5418 (Phase 1, lane A). Companion to #5453 (TTL sweep). ## What Nine localStorage stores grew without bound (full 58-call-site audit in the tracking issue). Each now has an explicit leak-guard cap, applied wherever the store is parsed, merged, or written, preserving each file's merge/versioning semantics: - **Community icons:** 32 entries, 96 KiB/value (aligned with the relay's `MAX_WORKSPACE_ICON_DATA_URL_LEN`); touched relay becomes newest. - **Channel mutes/stars:** newest-500 cap each, bounded by recency (`updatedAt`, channel-ID lexical tie-breaker), with the just-written channel unconditionally preserved for that write (cap−1 recency slots + the mutated key). A bounded LWW store cannot guarantee permanent deletion history; the guarantee here is that **the just-written mutation survives its own bounding** and, as the newest entry, defeats an older remote `true` through the pre-publish `mergeStores`. Known residual (accepted): `updatedAt` is whole-second, so two distinct mutations inside the same second at exact capacity can still evict the earlier one before the debounced publish — same root cause as the merge-path same-second tie, tracked for the follow-up precision fix rather than more preservation machinery. Enforced at parse, post-merge, local state, and persistence. - **Forced unread:** newest 500 insertion-ordered, touched channels refreshed. - **Persistent agent audiences:** 200-scope LRU. An unchanged-audience touch (including re-initializing an existing scope) refreshes LRU order and persists without advancing the scope's revision or emitting; an already-most-recent touch is a pure no-op (no clone, no write), so render-path re-initialization causes zero storage traffic. - **Self profiles:** newest 8 per relay / 32 globally by `updatedAt`, just-written key always preserved; trim count-gates before parsing payloads so under-cap writes skip the scan entirely. - **Sections:** newest 100 + newest 1,000 assignments, orphans removed; `assignChannel` delete/reinserts the touched channel so a reassignment becomes newest in insertion order and cannot be evicted by the next assignment. **Sort prefs:** 104 groups (100 sections + 4 fixed). - **Feature overrides:** `getOverrides()` filters to current-manifest boolean ids on read only — no write-back from the render-path getter. ## Review-driven revisions - `237f25e4` — three narrow changes from the first adversarial review (no render-path storage write, icon cap aligned to relay constant, count-gated profile trim). - `d864ffb0` — fixes for the two GitHub review findings on `237f25e4`: (P1) mute/star bounding switched from false-tombstone-first eviction to pure recency, with regressions proving an at-capacity unmute/unstar survives bounding and the pre-publish LWW merge; (P2) unchanged agent-audience touches now refresh LRU order (no revision advance, no emit), with a subscriber-mounted regression. - `3ddbb26d` — MRU guard from the second adversarial VERIFY: the P2 touch path skips clone/persist entirely when the scope is already most-recently-inserted, eliminating repeat synchronous localStorage writes from render-path effects. Test proves a non-MRU identical touch writes exactly once (scope persisted last) and an already-MRU touch writes zero times. - `e220ccd9` — fixes for the second GitHub review round (Carl, on Wes's behalf): (1) mute/star bounders preserve the just-mutated key so a same-second mutation at capacity survives its own bounding; merge/sync call sites unchanged; (2) `assignChannel` delete/reinserts the touched key so an at-capacity reassignment isn't evicted by the next new assignment. Regressions at storage and hook level for both; negative-control run of the 7 new tests against the old sources: 7 fail. ## Validation - Full desktop suite 4555/4555 at both `d864ffb0` and `3ddbb26d`, plus desktop-check/typecheck via the push gate; focused storage/audience tests 62/62 at `d864ffb0`, 14/14 audience suite at `3ddbb26d`. - Independent adversarial review: APPROVE at `88a55aee` (including 100 smoke E2E specs covering every seeded store, run manually since push hooks exclude Playwright), then a second VERIFY pass: **VERIFIED at `d864ffb0`** — P1/P2 confirmed closed via negative-control runs of the new suites against the old sources, plus smoke Playwright on the mute/star/audience specs (17 passed). That VERIFY requested one pre-merge change (no localStorage writes from the render path), landed as the narrow MRU guard in `3ddbb26d` within the reviewer's stated no-re-review boundary. A third VERIFY pass: **VERIFIED at `e220ccd9`** — both findings from the second GitHub review confirmed closed by sensitivity testing (new tests fail on old sources), hostile same-call section-trim case constructed and passed, full suite 4562/4562 re-run independently. Authored by Meeseeks (agent), reviewed by Beth (agent), integrated by Rick (agent). Discussion: Buzz channel time-based-localstorage-eviction, thread 0d85a73ca43e54748128f89c3512a4726131bf5473253395d46bf8f3a7b58bd4. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Meeseeks <2e96988f190ed1bd3c568760103aa4cadb2bc6195b832e252c984392c89039bd@buzz.block.builderlab.xyz>
74 lines
1.9 KiB
JavaScript
74 lines
1.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
addForcedUnreadSource,
|
|
boundForcedUnreadMap,
|
|
forcedUnreadMarker,
|
|
MAX_FORCED_UNREAD_ENTRIES,
|
|
removeForcedUnreadSource,
|
|
} from "./forcedUnreadStore.ts";
|
|
|
|
test("adding an Inbox owner preserves an existing manual force", () => {
|
|
const entry = addForcedUnreadSource(null, 120, "inbox");
|
|
|
|
assert.deepEqual(entry, {
|
|
markerAtWhenForced: null,
|
|
sources: ["manual", "inbox"],
|
|
});
|
|
});
|
|
|
|
test("clearing Inbox ownership leaves a manual force intact", () => {
|
|
const entry = {
|
|
markerAtWhenForced: 120,
|
|
sources: ["manual", "inbox"],
|
|
};
|
|
|
|
assert.deepEqual(removeForcedUnreadSource(entry, "inbox"), {
|
|
markerAtWhenForced: 120,
|
|
sources: ["manual"],
|
|
});
|
|
});
|
|
|
|
test("clearing manual ownership leaves an Inbox force intact", () => {
|
|
const entry = {
|
|
markerAtWhenForced: 120,
|
|
sources: ["manual", "inbox"],
|
|
};
|
|
|
|
assert.deepEqual(removeForcedUnreadSource(entry, "manual"), {
|
|
markerAtWhenForced: 120,
|
|
sources: ["inbox"],
|
|
});
|
|
});
|
|
|
|
test("clearing the only force owner removes the entry", () => {
|
|
const entry = {
|
|
markerAtWhenForced: 120,
|
|
sources: ["inbox"],
|
|
};
|
|
|
|
assert.equal(removeForcedUnreadSource(entry, "inbox"), undefined);
|
|
});
|
|
|
|
test("forced unread map keeps the newest 500 insertion-ordered entries", () => {
|
|
const map = Object.fromEntries(
|
|
Array.from({ length: MAX_FORCED_UNREAD_ENTRIES + 2 }, (_, index) => [
|
|
`channel-${index}`,
|
|
index,
|
|
]),
|
|
);
|
|
|
|
const bounded = boundForcedUnreadMap(map);
|
|
|
|
assert.equal(Object.keys(bounded).length, MAX_FORCED_UNREAD_ENTRIES);
|
|
assert.equal(bounded["channel-0"], undefined);
|
|
assert.equal(bounded["channel-1"], undefined);
|
|
assert.equal(bounded[`channel-${MAX_FORCED_UNREAD_ENTRIES + 1}`], 501);
|
|
});
|
|
|
|
test("legacy persisted entries retain their read-marker baseline", () => {
|
|
assert.equal(forcedUnreadMarker(120), 120);
|
|
assert.equal(forcedUnreadMarker(null), null);
|
|
});
|