mirror of
https://github.com/block/buzz.git
synced 2026-11-01 06:51:17 +00:00
Round-6 fixes for NIP-RS unread state (#3966): 1. Transaction abort/retry on failed cleanup writes abortTransaction() on both step-1 (register+receipt persist) and step-3 (cleanup persist) failure paths: gen1 is restored as the live intent, any promoted gen2 is re-buffered into deferredEnqueues, and scheduleDrain fires a retry pass. Promotion only happens inside a successful commit — durable gen1 and in-memory gen2 can never silently diverge. Retry mechanism: scheduleDrain() after abort, which fires pendingFreshDrain handling in drainPendingIntents (immediate re-drain in the same session; restart recovery handled by Amendment C receipts). Failure semantics on step-3: gen1 register is already durably committed (step-1 succeeded). The receipt is restored in-memory so the retry drain's alreadyApplied check fires (cleanup-only, no re-bump). The v2 blob retains gen1 pi+receipt until the cleanup write succeeds. 2. Generation hygiene Number.isSafeInteger() checks everywhere (pi.gen, receipt intentGen, ng). nextGen normalized to max(intent gens, receipt gens) + 1 on hydration, preventing a corrupt ng from reusing an existing generation (which would cause an old receipt to silently discard a fresh action via the Amendment C already-applied check). If the increment would overflow MAX_SAFE_INTEGER, falls back to a fresh safe value. 3. Production drain outcome handler extracted from hook createDrainOutcomeHandler() extracted as a pure typed function in useUnreadChannelsHelpers.ts. useDrainOutcomeCallback delegates to it. Tests import createDrainOutcomeHandler directly — no hand-copied mirror, no decorative drift guard. Tests (4762/4762 pass): - Tests 51/52: timing fixed — scheduleDrain intercepted to prevent the abort-path auto-retry from running before intermediate blob assertions; blockV2Writes persistent flag used for step-1 failure so both the initial throw and any recovery retry fail; manual retry fired after assertions. Both branches covered with restart witnesses. - Test 53: collision case (pi.gen=7, receipt gen=7, ng=7) — nextGen normalizes to 8+, fresh action gets gen > 7, old receipt does not match. - Test 54: scalar-number priorForcedEntry accepted as valid legacy entry. - Tests 45/46/47/50: now use createDrainOutcomeHandler from production. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Buzz
Desktop chat shell with:
- Tauri + React + TypeScript + Vite
- Tailwind CSS
- shadcn/ui-ready shared components
- Biome (lint/format/check)
- Feature-driven frontend structure
Scripts
pnpm dev- run the web frontendpnpm tauri dev- run the desktop apppnpm build- typecheck and build frontendpnpm typecheck- TypeScript checkspnpm lint- Biome lintpnpm format- Biome format (write)pnpm check- Biome check
Structure
src/shared- reusable app-wide code (ui,lib,styles)src/features- feature modules (vertical slices)src/app- top-level app composition