Skip to content

fix(agents): report a conversation wake that fails before its turn starts - #2192

Merged
rileycrane merged 1 commit into
mainfrom
rileycrane/report-failed-wakes
Nov 13, 2026
Merged

rileycrane merged 1 commit into
mainfrom
rileycrane/report-failed-wakes

Conversation

@rileycrane

@rileycrane rileycrane commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A timer or watch that delivers to a conversation can fail before its turn ever reaches the harness. Causes include:

  • protection is unavailable
  • host.start refuses
  • the saved root isn't in the channel
  • the 60s handover times out
  • the harness throws before handing off

The bundled harnesses report failures only inside their own handoff (report() in claude-code, goose and pi). This earlier path reaches only console.error("Agent run failed") in AgentRunners.start(), and the packaged app doesn't keep that.

I hit this on a one-shot Sunday timer in Foundation 0.7.0-preview.218.1:

  • the timer fired (lastWokeAt recorded, used: 1)
  • no harness process spawned
  • no transcript or error post appeared
  • the spent timer was never cleaned up

The occurrence was lost with no trace anywhere. A 60s repro timer in the same DM ran end to end, so the cause was intermittent. I couldn't confirm it after the fact, which is the point of this change.

Change

AgentRunners.start() now handles a non-addressed wake (timer, watch, catch-up or source) with a conversation destination whose completion rejects. The agent posts this in that conversation, mentioning its owner:

⚠️ My timer weekly woke me but its turn couldn't start: <error, up to 500 chars>

The notice is skipped when:

  • the agent's lifetime was aborted (stopped, retyped, removed, or the app quit)
  • the wake is no longer current
  • the agent isn't a member of the destination
  • the same wake has already reported. A wake that keeps failing reports once until one of its turns starts again, so a fast repeating timer can't spam its owner.

A root refused as "not in its channel" isn't replied to. The notice goes to the channel level instead.

docs/buzz-help.md gets one sentence about the notice.

Not changing

  • Giving the occurrence back. The failed occurrence stays used. Retrying from here risks a loop, and the owner can now see the failure and rearm.
  • Standalone wakes with no channel. They have nowhere to post.
  • Addressed triggers. Harnesses already report those.
  • Solo chats. A rooted timer pointed at a flat Solo chat would get a reply-tagged notice. The runner doesn't know soloChat here, so I left that case alone.
  • Goose with wait set. A rejected handed is only warned about, and run resolves, so that failure stays silent. That's a gap inside the harness, not this path.

Tests

src/features/agents/service.test.ts, describe("a timer turn that fails before it starts"), 4 cases:

  • reported once, at the top level and on a thread root
  • a repeat reports again only after a turn started (4 occurrences: fail, fail, succeed, fail → 2 notices)
  • a refused root reports at the channel level without an e tag

All 4 fail on main. Removing just the dedupe fails the repeat case, and removing just the refused-root handling fails that case.

Validation on eb1d64f (rebased onto main 8aa3adf35):

  • the 4 cases pass; all 4 fail on clean main 8aa3adf35
  • pnpm check (biome, typecheck, design checks) is clean
  • full vitest run: 21803 passed, 10 failed

Two of the failures also fail on clean main 8aa3adf35 on this machine:
browser-host/media-preparation.integration.test.mjs (tiled HEIC
dimensions) and browser-host/vite-config.test.mjs (a spawnSync error).
The other 8 are timeouts in entity-navigation, DraftPreview,
MessageComposer and store-discovery, none of them under agents. That
run shared the machine with a full main run. Rerun alone on this branch,
those 4 files pass, 770 of 770.

How to test

  1. Run just dev, enable Attention on an agent, and give it a channel you're both in.
  2. Make its turn fail before handoff. The easiest way is a timer with a root that isn't in that channel. Ask the agent to set interval_secs: 60 and root: <any 64-hex id> with channel: <the channel>.
  3. After about a minute, one ⚠️ "couldn't start: The delivery root is not in its channel" message should appear at the channel level, mentioning you.
  4. With max_occurrences: 3, you should still see only one notice.

🤖 Generated with Claude Code

…arts

A timer or watch that delivers to a conversation can fail before its
turn reaches the harness: the agent's protection is unavailable, its
start is refused, its root is not in the channel, or the handover
times out. The bundled harnesses report failures only inside their own
handoff, so this path reached only console.error, which the packaged
app does not keep. The occurrence was spent with no trace.

The runner now posts a ⚠️ notice in that conversation that mentions the
owner, once until one of the wake's turns starts again. It is not sent
when the agent stopped, the wake is no longer current, or the agent is
not a member there. A refused root is not replied to. The occurrence
is still used, not retried.

Co-authored-by: Opus 5.5 Max <d9fc2aa400ad81a2f289921a1720811e5d922c43e079f37241d4be458d8d92aa@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Riley Crane <[email protected]>
@rileycrane
rileycrane force-pushed the rileycrane/report-failed-wakes branch from 6e43b8f to eb1d64f Compare November 13, 2026 20:06
@rileycrane
rileycrane marked this pull request as ready for review November 13, 2026 20:06
@rileycrane
rileycrane requested a review from a team as a code owner November 13, 2026 20:06

@wpfleger96 wpfleger96 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Looks good. I traced the new notice against the claude-code, goose and pi handoffs: none of them posts its own error and then rejects run, so this can't double up with a harness report. A protection failure still gets its notice, since publish doesn't go through the protection check. One nonblocking suggestion inline.

private readonly failedSources = new WeakSet<AttentionObject>();
/** Wakes whose failure to start was reported, until one of their turns
* starts again, so a failing repeat reports once. */
private readonly reportedWakes = new Set<string>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Nonblocking: keying this on ${agent.key}\0${slug} lets the dedupe outlive the timer it was about. setAttention writes a new AttentionObject on every edit or rearm, but the slug stays the same. So this sequence stays silent: a one-shot timer fails and the owner gets a notice, the owner fixes its root or rearms it, and it fails again before any turn starts. That's the recovery the notice points people to, and it ends up losing the occurrence the same way main does. The set also never loses entries for timers that are removed while they're still failing.

If you key it on job.matched.object in a WeakSet<AttentionObject>, the same way failedSources works, a repeating timer still reports once, since its object stays the same across occurrences. Any edit or rearm resets it, and entries go away with the object. object.slug and object.value.type would also give you what/slug for the message, so you could drop the trigger.type narrowing in reported.

@rileycrane
rileycrane merged commit 30ae25f into main Nov 13, 2026
64 checks passed
@rileycrane
rileycrane deleted the rileycrane/report-failed-wakes branch November 13, 2026 21:22
joshfriend added a commit that referenced this pull request Nov 13, 2026
…when-ready

* origin/main: (21 commits)
  fix(agents): report a conversation wake that fails before its turn starts (#2192)
  fix(usage): read Claude transcripts over 50 MB a page at a time (#3498)
  feat(agents): preview where interrupted agents were working before resume (#3494)
  perf(messages): close the editor and delete dialog on submit (#3493)
  feat(messages): quote selected message text into the composer (#2392)
  perf(toast): scope docked clearance to the toast host (#3489)
  fix(unread): undo exact captured ordinary reads conditionally (#3473)
  fix(solo): keep navigation responsive during chat deletion (#3488)
  fix(channels): keep custom thread tab names until the tab closes (#3458)
  perf(relay): verify cached history in the native host (#3426)
  fix(settings): refine claude and typesafe key setup dialogs (#3391)
  Update bundled help for Bestie and agent settings (#3194)
  feat(profiles): share and show a local time zone (#3332)
  Revert "feat(link-previews): unfurl web links below messages by default" (#3480)
  Show disabled plugin chats in Solo (#3456)
  perf(messages): host one context menu per message list (#3465)
  test(claude): keep the quit-release test copy above the minimum version (#3477)
  fix(profiles): restore verified names from the device cache (#3382)
  fix(mentions): show addressed agents first in Solo audience picker (#2856)
  perf: count member access once and scope find highlights to painting (#3464)
  ...
joshfriend added a commit that referenced this pull request Nov 13, 2026
…n-on-github

* origin/main: (21 commits)
  fix(agents): report a conversation wake that fails before its turn starts (#2192)
  fix(usage): read Claude transcripts over 50 MB a page at a time (#3498)
  feat(agents): preview where interrupted agents were working before resume (#3494)
  perf(messages): close the editor and delete dialog on submit (#3493)
  feat(messages): quote selected message text into the composer (#2392)
  perf(toast): scope docked clearance to the toast host (#3489)
  fix(unread): undo exact captured ordinary reads conditionally (#3473)
  fix(solo): keep navigation responsive during chat deletion (#3488)
  fix(channels): keep custom thread tab names until the tab closes (#3458)
  perf(relay): verify cached history in the native host (#3426)
  fix(settings): refine claude and typesafe key setup dialogs (#3391)
  Update bundled help for Bestie and agent settings (#3194)
  feat(profiles): share and show a local time zone (#3332)
  Revert "feat(link-previews): unfurl web links below messages by default" (#3480)
  Show disabled plugin chats in Solo (#3456)
  perf(messages): host one context menu per message list (#3465)
  test(claude): keep the quit-release test copy above the minimum version (#3477)
  fix(profiles): restore verified names from the device cache (#3382)
  fix(mentions): show addressed agents first in Solo audience picker (#2856)
  perf: count member access once and scope find highlights to painting (#3464)
  ...
joshfriend added a commit that referenced this pull request Nov 13, 2026
* origin/main: (31 commits)
  Let plugin panels name their tab with panels.useTabTitle (#3251)
  fix(agents): report a conversation wake that fails before its turn starts (#2192)
  fix(usage): read Claude transcripts over 50 MB a page at a time (#3498)
  feat(agents): preview where interrupted agents were working before resume (#3494)
  perf(messages): close the editor and delete dialog on submit (#3493)
  feat(messages): quote selected message text into the composer (#2392)
  perf(toast): scope docked clearance to the toast host (#3489)
  fix(unread): undo exact captured ordinary reads conditionally (#3473)
  fix(solo): keep navigation responsive during chat deletion (#3488)
  fix(channels): keep custom thread tab names until the tab closes (#3458)
  perf(relay): verify cached history in the native host (#3426)
  fix(settings): refine claude and typesafe key setup dialogs (#3391)
  Update bundled help for Bestie and agent settings (#3194)
  feat(profiles): share and show a local time zone (#3332)
  Revert "feat(link-previews): unfurl web links below messages by default" (#3480)
  Show disabled plugin chats in Solo (#3456)
  perf(messages): host one context menu per message list (#3465)
  test(claude): keep the quit-release test copy above the minimum version (#3477)
  fix(profiles): restore verified names from the device cache (#3382)
  fix(mentions): show addressed agents first in Solo audience picker (#2856)
  ...
philazar added a commit that referenced this pull request Nov 13, 2026
…ve-client

* origin/main:
  feat(solo): smooth chat recency reordering (#3502)
  Reject self-mentions that cannot wake another session (#3513)
  feat(status): offer don't clear for every status (#3515)
  Let plugin panels name their tab with panels.useTabTitle (#3251)
  fix(agents): report a conversation wake that fails before its turn starts (#2192)
  fix(usage): read Claude transcripts over 50 MB a page at a time (#3498)
  feat(agents): preview where interrupted agents were working before resume (#3494)
  perf(messages): close the editor and delete dialog on submit (#3493)
  feat(messages): quote selected message text into the composer (#2392)
  perf(toast): scope docked clearance to the toast host (#3489)
  fix(unread): undo exact captured ordinary reads conditionally (#3473)

Signed-off-by: Philip Azar <[email protected]>
wpfleger96 added a commit that referenced this pull request Nov 14, 2026
…y-unreads

* origin/main: (24 commits)
  [1/2] feat(agents): add a recommended agent setup (core) (#3344)
  test(browser): survive the live-stream restart a page-clock jump causes (#3442)
  chore(renovate): hold rusqlite below 0.41 for mesh-llm (#3438)
  test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (#3445)
  test(browser): play the draft clip at rate 0 instead of looping it before the Play click (#3446)
  fix(status): reopen a status whose preset deadlines coincide as Today (#3449)
  fix(messages): tolerate a cancelled reaction-name chunk load (#3450)
  fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (#3451)
  test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (#3444)
  Expose native channel headings to Solo sources (#3523)
  test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (#3447)
  fix(solo): keep every contributed destination action (#3516)
  fix(identity): report an issuer that keeps rate-limiting an agent as a limit (#2365)
  fix(identity): let a stuck at-launch sign-in copy its link (#2453)
  fix(profiles): open avatar previews on hover in compact density (#3510)
  feat(solo): smooth chat recency reordering (#3502)
  Reject self-mentions that cannot wake another session (#3513)
  feat(status): offer don't clear for every status (#3515)
  Let plugin panels name their tab with panels.useTabTitle (#3251)
  fix(agents): report a conversation wake that fails before its turn starts (#2192)
  ...

# Conflicts:
#	tests/browser/message-management.spec.mjs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants