Repository navigation
feat(attention): Interest titles, summaries and named people, and Activity and pause fixes - #2860
Conversation
Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Arjun Mahanti <arjun@squareup.com>
An Activity row's summary and accessible label used the raw first line of a watched message, so `Status: ||private result||` showed the hidden text before anyone revealed it. Summaries now use the shared plain-text preview that notifications and Sent use, which shows `[Spoiler]`. Command output stays literal. Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
Co-authored-by: Codex <noreply@openai.com> Signed-off-by: Arjun Mahanti <arjun@squareup.com>
A timer wake saved only `lastWokeAt`, so a timer that had fired still said "No activity yet". Each wake is now recorded as an Activity entry holding the timer's prompt, the way a watch records its event. The count also reads "1 event" rather than "1 events". Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
An Interest held only its instructions, so its card guessed a title from a leading heading or the slug and gave no quick sense of what the Interest does. Interests now take an optional title and summary, which interest_set accepts and the card shows above the watches. An Interest without a summary shows none, and one without a title keeps the old heading or slug label. interest_set asks agents to fill both in. Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
Some MCP clients send an integer whose schema also allows a word (max_occurrences, expires_at, since) as a string, so watch_add rejected every timer budget they gave. Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
The tool dropped gh's error and always answered "Could not read the pull request with gh", so an agent could not tell a missing repository from an expired login or a storage failure. It now includes gh's message. Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
Both add optional Interest fields, so an Interest now keeps title, summary and paused together: interest_set writes the given title and summary and carries over the current paused flag. Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@squareup.com>
"Delete interest and watches" was too wide for the confirmation dialog, so the button wrapped onto its own row under Cancel. The title already names the Interest and the description says its watches go too. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🤖 @mahanti, could you review the design of this one? It's the follow-up to your pause controls: Interest titles and summaries, hidden spoilers and fired timers in Activity. The screenshot is in the description. I'm also adding fixes here for Catherine's three findings on your PR, which don't change the UI. |
A watch over the limit is kept as skipped and written back as read. Pausing its Interest left that copy plain, so an older reader with a higher limit could run it without its instructions, and deleting the Interest left it behind to run later. Envelope skipped children of a paused Interest like kept ones, unwrap them on resume, and delete them with their Interest. An enveloped watch also kept its history at the top level as well as inside the envelope, which could push a paused record over the 4 MiB storage limit. Save it once, in the envelope. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Interest instructions written by agents were full of raw IDs. A person written as [Name](nostr:npub…) now shows as the name and, where the host can, opens their profile: beside the conversation in the profile panel, or by selecting that agent on the Agents page. Plugin agent tabs receive an optional open(target) for this. The interest_set tool asks agents to name people that way and to link PRs, messages and channels instead of pasting IDs. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 Three findings: active-Interest downgrade loses task instructions, profile navigation discards unsaved edits, and human profile links do nothing in Agents.
An older build's strict Interest validator rejects a title or summary, but the Interest's event and timer watches stayed readable there, and its scheduler runs a watch whose Interest is missing, so they woke the agent without its instructions. Save the watches of an Interest an older reader cannot apply in the envelope already used for a paused Interest, which keeps their timer state and history, and unwrap it again on read. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rofile In a conversation-side owned-agent profile, opening a person from an Interest's instructions pushed their profile at once, so an unsaved name draft was lost when the agent's profile was recreated on Back. Ask with the Agents page's discard prompt while the profile reports unsaved edits, and open the link only once they are discarded. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Agents page linked every person in an Interest's instructions, but it can only open agents in its own list, so a human's name was a button that did nothing. Pass each host's canOpen alongside open, and render a person as a button only where the host can open them, plain text otherwise. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@builderbot review |
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 The three previously reported P2s are fixed in the reviewed paths. One non-blocking contract-documentation issue remains.
mahanti
left a comment
There was a problem hiding this comment.
Looks good, thanks for the update!
The tab contract now passes canOpen beside open, and open can return false while the host asks whether to discard unsaved edits, then open the link afterwards. Watches of an active Interest with a title or summary are now saved in the same envelope as a paused Interest's, so older readers skip them; say so in the parity notes. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@builderbot review |
1 similar comment
|
@builderbot review |
interest_remove counted only readable watches, so an Interest whose remaining watch was skipped (for example over the limit) was removed and left that watch without an Interest, where the agent cannot delete it. Refuse instead and point to the owner's Attention tab, which deletes skipped watches with their Interest. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The confirmation counted only readable watches, so deleting an Interest whose remaining watches were skipped removed them without asking. Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Poppy <ace4dfe51b23bb4244ab9117dfd5e13b8239445e333225608c352e68cb3fe2b5@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@builderbot review |
loganj
left a comment
There was a problem hiding this comment.
🤖 Larry and Auto: an independent adversarial review of fa5e0a6, rechecked at be19487. Source review only, plus Auto's local run at fa5e0a6 (vitest over src/features/agents, src/bundled/{watching,agents,profiles,github} and src/buzz-mcp: 2442 passed, 1 skipped; tsc --noEmit clean). The UX review we asked for has not arrived yet.
Verdict: approve. No must-fix findings.
Fixed since fa5e0a6 (checked in the delta):
interest_removenow refuses while a skipped child watch remains, so an agent can no longer leave an over-limit watch with no Interest (a2199a6).- The Delete confirmation now counts skipped children, sharing one helper with the delete path (be19487).
Left for the follow-up the author named. Inline comments mark each one; all are optional:
- advice:
WatchRow.tsx:97-100(not in this diff) still labels a nameless watch rowtitle(watch.interest_id). A card titled "Release checks" with idrel-cihas rows and aria-labels that say "rel ci" ("Delete rel ci"). Share the card's rule (title, then heading, then slug) fromTab.tsx. - note: once agents set titles, every titled Interest's watches are enveloped, so any build older than this one (including dev and PR builds on the same storage) skips them. The description states this; it is a rollout note, not a defect.
| if (parent?.type !== "interest") return value; | ||
| if (sealed(attention, watch.interest_id)) | ||
| return { ...watch, interest_paused: interest_paused ?? {} }; | ||
| return watch; |
There was a problem hiding this comment.
🤖 advice (optional): when the Interest becomes readable again (resumed, or its title and summary cleared), this returns watch and discards interest_paused.{timer,history,completed}. readRecord keeps top-level timers only for live slugs, so nothing else holds that state. An over-limit timer in a paused Interest loses used on resume, so a reader with a higher limit restarts its max_occurrences budget. Rare; fine for the follow-up.
There was a problem hiding this comment.
🤖 Agreed, it's a real gap. It only hits an over-limit timer inside a paused Interest that is later resumed, so I'm deferring it to the follow-up rather than changing this approved head. Leaving this open.
| <span>{children}</span> | ||
| ); | ||
| return ( | ||
| <a href={href} target="_blank" rel="noreferrer"> |
There was a problem hiding this comment.
🤖 question (optional): only nostr:npub1… is routed in-app. nostr:nprofile1… and buzz:// message and channel links still open through target="_blank", but interest_set now asks agents to "link pull requests, messages and channels". Activity already routes buzz: links in-app with buzzLinkTarget (Activity.tsx:156); the instructions renderer could do the same, and show other nostr: links as text.
There was a problem hiding this comment.
🤖 Yes. nprofile should be treated like npub, and buzz: links should open in the app the way Activity does. This PR only promised npub person links, and the other schemes still open externally as they did on main. Deferred to the follow-up; leaving this open.
| slug, | ||
| { | ||
| type: "interest", | ||
| ...given(args, "title"), |
There was a problem hiding this comment.
🤖 question (optional): paused survives an instructions-only interest_set, but an omitted title or summary is cleared. An agent that updates only its instructions (older prompts, Janet habits) silently turns the card back into the slug. Keeping them when omitted and clearing them on an explicit "" would match paused and be harder to misuse.
There was a problem hiding this comment.
🤖 It's deliberate and documented, and matches how watches are replaced. Keeping title and summary when they're omitted, and clearing them on an explicit empty string, is a reasonable change. Deferred to the follow-up; leaving this open.
| <span className="attention-detail-preview"> | ||
| {history?.events?.length | ||
| ? `${history.events.length} events` | ||
| ? `${history.events.length} event${history.events.length === 1 ? "" : "s"}` |
There was a problem hiding this comment.
🤖 advice (optional): timers that fired before this build have only lastWokeAt in history, so they still say "No activity yet" until they fire again. A fallback such as "Last fired " when lastWokeAt is set would cover them.
There was a problem hiding this comment.
🤖 Right: timers that fired before this build show the old text until they fire again. A lastWokeAt fallback goes in the follow-up. Leaving this open.
| const summary = | ||
| (command | ||
| ? heading(entry.text.trim().split("\n")[0] ?? "").text | ||
| : plainText(entry.text)) || "Recorded event"; |
There was a problem hiding this comment.
🤖 advice (optional, perf): plainText(entry.text) parses up to 4 KB of Markdown per row on every render, unmemoized, each time the agents snapshot changes. Before, heading() was a regex on one line. useMemo on entry.text covers it.
There was a problem hiding this comment.
🤖 Activity lists are short, so I'll add the useMemo in the follow-up rather than change this head. Leaving this open.
|
🤖 The deferred findings (Auto's 2, 3, 4, 5, 6 and 8, and the five optional inline notes) are tracked in #2903. |
Co-authored-by: Simone <0828be588d5c9c4d092c5b1cf01761d5aef63d6b104807fd8e8284d517dd1a99@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * origin/main: (22 commits) feat(agents): squircle agent avatar previews (#2879) fix(todos): show Todos read-only without Canvas write access (#2799) fix(unread): leave mentions that arrive during catch-up unread (#2801) Show one working indicator while a message's live activity shows (#2851) fix(agents): ask before deleting a team (#2820) fix(dialog): keep stepped dialog cards top-anchored during crossfade (#2893) feat(attention): Interest titles, summaries and named people, and Activity and pause fixes (#2860) fix(messages): Enter on the outside-mention prompt invites again (#2897) feat(agents): keep open agent transcripts current while the agent works (#2844) perf(solo): render a sidebar chat row only when it changes (#2505) fix(solo): restore agent selection during slow background reads (#2751) feat(search): preview unread thread replies in cmd+k (#2743) fix(plugins): suggest SSH when a private HTTPS clone needs credentials (#2455) fix(browser-host): use the Buzz login variant for dev Builderlab sign-in (#2222) refactor(plugin-manager): move distributor values into build settings (#2649) fix(communities): let Cancel, × and Escape leave a Join lookup and name the host that didn't answer (#2821) fix(agents): let watches hear public channels the agent is not in (#1854) Preview and send Drafts & sent in place, with a shared count bubble (#2833) feat(terminal): add an Option as Meta key setting on macOS (#2850) fix(agents): restrict workflow requests in bb-block only (#2518) ... Signed-off-by: John Tennant <jtennant@squareup.com> # Conflicts: # src/bundled/agents/AgentAvatarPicker.tsx # src/bundled/agents/AgentCreate.module.css
Co-authored-by: Simone <0828be588d5c9c4d092c5b1cf01761d5aef63d6b104807fd8e8284d517dd1a99@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * origin/main: (22 commits) feat(agents): squircle agent avatar previews (#2879) fix(todos): show Todos read-only without Canvas write access (#2799) fix(unread): leave mentions that arrive during catch-up unread (#2801) Show one working indicator while a message's live activity shows (#2851) fix(agents): ask before deleting a team (#2820) fix(dialog): keep stepped dialog cards top-anchored during crossfade (#2893) feat(attention): Interest titles, summaries and named people, and Activity and pause fixes (#2860) fix(messages): Enter on the outside-mention prompt invites again (#2897) feat(agents): keep open agent transcripts current while the agent works (#2844) perf(solo): render a sidebar chat row only when it changes (#2505) fix(solo): restore agent selection during slow background reads (#2751) feat(search): preview unread thread replies in cmd+k (#2743) fix(plugins): suggest SSH when a private HTTPS clone needs credentials (#2455) fix(browser-host): use the Buzz login variant for dev Builderlab sign-in (#2222) refactor(plugin-manager): move distributor values into build settings (#2649) fix(communities): let Cancel, × and Escape leave a Join lookup and name the host that didn't answer (#2821) fix(agents): let watches hear public channels the agent is not in (#1854) Preview and send Drafts & sent in place, with a shared count bubble (#2833) feat(terminal): add an Option as Meta key setting on macOS (#2850) fix(agents): restrict workflow requests in bb-block only (#2518) ... Signed-off-by: John Tennant <jtennant@squareup.com> # Conflicts: # src/bundled/agents/AgentCreate.module.css
…ion-and-status * origin/main: feat(agents): team instructions history and agent/team config tools (#2848) test(plugin-manager): use an immutable git socket fixture (#2665) fix(attention): show the attention toggle when an agent has nothing yet (#2907) fix(agents): stream the remote agent introduction 1.5× faster (#2902) fix(relay): stop holding a watched public channel that a read after a reconnect leaves out (#2889) feat(agents): squircle agent avatar previews (#2879) fix(todos): show Todos read-only without Canvas write access (#2799) fix(unread): leave mentions that arrive during catch-up unread (#2801) Show one working indicator while a message's live activity shows (#2851) fix(agents): ask before deleting a team (#2820) fix(dialog): keep stepped dialog cards top-anchored during crossfade (#2893) feat(attention): Interest titles, summaries and named people, and Activity and pause fixes (#2860) fix(messages): Enter on the outside-mention prompt invites again (#2897) feat(agents): keep open agent transcripts current while the agent works (#2844) perf(solo): render a sidebar chat row only when it changes (#2505) fix(solo): restore agent selection during slow background reads (#2751) feat(search): preview unread thread replies in cmd+k (#2743) fix(plugins): suggest SSH when a private HTTPS clone needs credentials (#2455) # Conflicts: # src/features/agents/team-ops.ts
Follow-ups to the Attention card refresh and per-Interest pause controls.
Problem
# headingor the slug, so you had to open the instructions to learn what an Interest does.Status: ||private result||appeared in full in the row and its accessible label.watch_addrejected timer budgets from MCP clients that send numbers as digits, andgithub_watch_prhid gh's error, so an agent could not tell a missing repository from an expired login.[Name](nostr:npub…)showed as the name, but clicking it tried to open an external app.Solution
interest_setaccepts both and asks agents to fill them in, and the card shows the title with the summary under it. Existing Interests look as they do today until their agent next updates them.[Spoiler].watch_addaccepts numbers sent as digits, andgithub_watch_princludes gh's message in its error.[Name](nostr:npub…)in Interest instructions shows as the name and opens their profile: beside the conversation in the profile panel, or by selecting that agent on the Agents page. Plugin agent tabs get an optionalopen(target)for this, andinterest_setasks agents to name people that way and to link PRs, messages and channels instead of pasting IDs.interest_removerefuses while one remains, rather than orphaning it. A paused watch's history is saved once, inside the envelope.Compatibility: a build without this change skips an Interest saved with a title or summary, the same fail-closed path paused Interests already use.
Reproduction
||secret||, then open its Activity. Before: the row readssecret. After:[Spoiler].Testing
Red/green: the spoiler, fired-timer and timer-wake tests fail on main and pass here, as does the overflow reproducer from the review of the pause controls, run against the actual older reader.
src/features/agents,src/bundled/watchingandsrc/buzz-mcppass (2,093 tests),tsc --noEmitand Biome are clean, andtests/browser/agent-attention.spec.mjspasses in Chromium and WebKit, including clicking a person in the instructions.Screenshots
A titled Interest with its summary, below an older Interest without them
Titled Interests with summaries, triggers and sources in the app
A person in the instructions, shown by name
🤖 Generated with Claude Code