Repository navigation
feat(notifications): notify on posts by chosen people - #2939
Open
LordMelkor wants to merge 2 commits into
Open
LordMelkor wants to merge 2 commits into
LordMelkor wants to merge 2 commits into
Conversation
Contributor
|
🤖 Design review: this isn't critical path for the Nov 10 launch. Please hold it until after launch, or build it as an external plugin that isn't bundled into the app. Questions: #buzz-design-review. |
Notification choices were four category switches plus channel mute, so there was no way to hear about someone's posts without being mentioned. Add a persisted people list and a `person` category: a listed person's post in a channel the viewer is in alerts under the same viewing, unread and mute gates, and alerts once as a mention when it also mentions the viewer. Toggle from the profile panel; review and clear in Settings → Notifications. Co-authored-by: Clod <c7b21872e9ddb79ca62967d0e21e6a4518a669eb04648e963cdfb2426e62fafb@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: LordMelkor <kray@squareup.com>
…ead opt-outs A listed person's post relayed under the relay's signature (actor or leading p) now matches the verified displayed author. A broadcast reply in a thread the viewer opted out of stays quiet for person alerts, while broadcast unread counting is unchanged. Colliding display names in the people list show a key in the row and the Remove label. Co-authored-by: Clod <c7b21872e9ddb79ca62967d0e21e6a4518a669eb04648e963cdfb2426e62fafb@buzz.block.builderlab.xyz> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: LordMelkor <kray@squareup.com>
LordMelkor
force-pushed
the
clod/notify-people
branch
from
November 10, 2026 16:39
d56eca5 to
09209d3
Compare
kazemek21
added a commit
that referenced
this pull request
Nov 10, 2026
## TL;DR Captured explicit reads now check the current saved manual-unread journal inside the replacing transaction, so a newer local choice—including one saved by another window—is not erased. Retries retain their original cut; the additive `unread.prepareThreadRead(channelId, rootId)` captures only retained incoming replies without reading the root or advancing the channel. The fence also preserves earlier same-window queued manual actions, authorizing their revisions only after a successful save whose prior same-key intent is also captured. An earlier queued save cannot mask an intervening unread choice from another window. It leaves reading-lease ownership, passive observations, remote-marker ordering, journal format and relay protocol unchanged. Mark all now reserves one shared saved revision cut before queued writes. Each predicate retains only the revision, not a full journal copy. ### Validation - Shared-cut correction: pinned Linux Blox run of both complete owner files passed 383 tests (174 read-state; 209 unread). The per-sweep capture-budget and capture-failure regressions fail on the prior implementation; older queued-save controls pass. No new browser cases. - Linux Blox, pinned tools: both owner files passed 366 tests on the initial head. After review fixes, `pnpm exec vitest run src/features/relay/unread.test.ts` passed all 206 tests. The final queue-ordering fix passes all 174 tests in `src/features/relay/read-state.test.ts`, typecheck and the commit/push hooks. - Pre-fix channel regressions: six failures; bypassing the new transaction fences makes eleven targeted concurrency regressions fail. Replacing canonical thread selection with parent-edge traversal makes the narrow-thread regression fail. Restoring the implementation passes both owner files. - Three successful-save interleavings fail before the final fix and pass afterward, with three matching controls: channel, thread and message marks overwritten by an older queued save after another window marks unread. - Eight additional regressions fail before the review fixes and pass afterward: root/reply deletion before or after persistence, and shared sidebar hints with absent, present or cross-window forced-message overlays. - Touched-file Biome, `pnpm typecheck`, `pnpm author:build`, strict generated-SDK consumer compilation and repository commit/push hooks. - Deterministic fixture gates cover newer channel/thread/message marks, second-window saves at the transaction boundary, queued ordering, retry cuts, later arrivals, independent root/thread markers and access/lifecycle expiry. No browser tests were added or removed. Generated type-only SDK declarations expose the additive method through the existing `UnreadCapability` export; existing channel and message APIs remain compatible. Capture requires initialized read-state storage. The thread API does not discover history; receipts remain device-local. A narrow read leaves the shared sidebar hint until channel open reconciles it, since that hint has no subtree ownership. Its shared message-force fence is conservative: a newer mark elsewhere in the same channel can also reject the save. This does not establish native runtime behavior, identical remote-tuple ordering or atomicity through an operation's eventual return. Related independent work remains with #2060 (quiet Solo/Inbox), #2610 (selected-message UI), #3181 (passive recovery), and #2939/#1666/#2956 (notification/audience behavior). This change is limited to the explicit mutation/preparation seam. Fixes NEXUS-4426 [Initiative status](https://g2.sqprod.co/apps/kylez-buzz-improvements-status) *Posted by Codex (gpt-6.1-sol) on behalf of kylez.* Signed-off-by: Kyle Zemek <kylez@squareup.com> Co-authored-by: Codex <noreply@openai.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Notification controls are four category switches (mentions, direct messages, thread replies and agent completions) plus channel Mute. There's no way to be notified when a particular person posts. An ordinary post in a channel you're in never alerts.
Change
personcategory. Stop notifying removes them.person:thread, including while its conversation lookup is pending.notificationPeopleservice's methods reject calls made through any plugin's context except the bundled Profiles plugin's (buzz.profiles), the same checkreview-access.tsuses for plugin management. The typedctx.notificationscontract has no people methods. This isn't isolation. Plugins are trusted, unsandboxed code (docs/plugin-architecture.md), so a plugin that digs into host objects, rewrites its own context or keeps another plugin's rendered panel can still reach the list, as it can every other notification preference.docs/notifications.mdanddocs/buzz-help.md.No FOUNDATION file is changed.
Intent
Original request: kray's #buzz-feedback post (Nov 2): "Would be great to be able to customize notifications: eg. channel activity vs mentions, posts by certain people". This PR is the "posts by certain people" half. A per-channel "all activity" mode is a separate PR.
Validation
messages.people.test.ts:thread.tsc --noEmitandbiome checkpass. Full suite at9f169d66, on mainb1e1cd82: 19,408 passed and 5 failed, in 3 files this diff doesn't touch (App.profile,IdentitySettings,MessageReactions). Run alone, those 3 files pass 89/89, so the failures are timeouts on a heavily loaded shared machine. After rebasing onto main41e64bf0and the round-2 fixes, TypeScript passes and the notifications, profiles, unread and settings tests pass 522/523. The one failure is a 5 s timeout inunread-startup.test.ts, which passes 52/52 when run alone.Round 1 (
sq agents review): three P2s, all fixed.Round 2 (
sq agents review): one P2, fixed. Any plugin could call the people-list service. Its methods now reject calls from every plugin except Profiles, and the profile toggle's test checks that another plugin is refused.Round 2 pressure test (Sigil): found that a plugin can still reach the list at runtime through
ctx.root, the untypedctx.notificationsobject, a context it rewrote withctx.extend(), or the Profiles panel's rendered props fromctx.panels. That is the trusted-plugin model the whole plugin API has, not a gap in this change, so the docs and this body now say exactly what the check covers and that it isn't isolation. Making plugins untrusted is out of this card's scope.Round 3 (
sq agents review): two P2s, both fixed.A third finding repeated the plugin-trust route, which is handled above.
Round 4 (
sq agents review): one P2, fixed. With 200 people listed, adding one reported a failed save, and Retry saving cleared the error without adding anyone. The card now says the list is full and offers no retry.Round 5 (
sq agents review): one P2, fixed. In Settings, two unnamed people whose npubs end alike got the same label and Remove button name. The list now usespublicKeyLabels, which lengthens colliding abbreviations.Final
sq agents reviewatd56eca58, on main41e64bf0: no findings.Rebase review (
sq agents review) at03a10cb5, after squashing and rebasing onto mainb1436b09: three P2s, all fixed in09209d36. Each has a test that fails without its fix. Production change: about +26/−10.actoror leadingptag) didn't alert. Matching now uses the author Buzz displays, from the existingmessageAuthorresolver. The same tag from any other signer stays untrusted.Not changed: an alert for a relay-signed post names the signer, as every other alert category does on main.
Full suite at
09209d36: 20,475 passed and 25 failed, in 6 files this PR doesn't touch (App.profile,ProfileSettings,solo-worker,DraftPreview,MessageReactions,relay/startup). Run alone, those 6 files pass 206/206, so the failures are timeouts under machine load.Pressure test: an independent agent (Sigil) found no blocker at the round-1 head. Later it confirmed both round-3 fixes: each regression fails on the parent, and reversing only the retry handler fails at the saved-choice assertion. It also confirmed that the trust-model wording matches its four access probes, and found nothing else. 338 tests, TypeScript and Biome pass at
b1ceb869(nowc9e29980after the rebase). At the final head (0c6cec09) it confirmed the full-list message, that freeing a slot lets a 200th entry save, and that removal works at capacity. 339 tests in 29 files, TypeScript and Biome pass. Round 1 also checked that pending lookups keep thread precedence, that removing a person cancels their waiting alert, and that an opted-out thread stays quiet even with Notify while viewing on. 268 tests in 23 files and TypeScript pass. Native notification delivery wasn't exercised.Try it (AGENTS.md step 3, for a human)
🤖 Generated with Claude Code