fix(buzz-acp): wake held ACP threads and fence forked sessions - #7340
Conversation
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
🔐 Codex Security Review
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38cecfa0a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| _ => std::future::pending().await, | ||
| } | ||
| } => None, | ||
| _ = pool::AgentPool::wait_for_hold_deadline(hold_deadline), if pool_ready => { |
There was a problem hiding this comment.
Clear deadlines when queue eviction retires a held scope
Under thread policy while all workers are busy, a held scope's only event can be the globally oldest item evicted by EventQueue::enforce_channel_cap when the channel reaches 500 queued events. That eviction removes the queue entry without clearing pool.held_since, so this new timer becomes permanently ready; HoldDeadline repeatedly calls dispatch_pending, which cannot revisit the missing scope and therefore never removes the timestamp, causing an indefinite CPU-intensive loop. Prune the hold when its queue scope is retired, or validate that a held scope still has pending work before scheduling its deadline.
AGENTS.md reference: AGENTS.md:L176-L186
Useful? React with 👍 / 👎.
| // A held thread must make progress even when every unrelated | ||
| // relay/timer source is quiet. The deadline is derived from | ||
| // the pool's first-held stamp, so this dispatch observes | ||
| // `ForkAfterHold` and claims an idle worker immediately. | ||
| for (scope, thread_tags) in dispatch_pending( |
There was a problem hiding this comment.
Preserve an expired hold until a worker can claim it
When this deadline fires while every pool slot is still checked out, dispatch_pending observes ForkAfterHold, which clears held_since, but then try_claim fails and the batch is requeued. If a different worker becomes idle while the original owner remains busy, the next dispatch stamps a fresh hold and waits another full 10 seconds even though the original bound already expired; unrelated work can repeatedly claim that worker and extend the delay further. Keep the expired state until a fork is successfully dispatched, or avoid consuming the deadline when no worker is claimable.
AGENTS.md reference: AGENTS.md:L194-L201
Useful? React with 👍 / 👎.
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
|
🤖 Reviewed Recommendation: fix the existing P1 before merging. I validated both existing Codex findings; I found no additional defect in the timer/dispatch and ownership-generation paths reviewed, so I am not adding duplicate inline comments.
The original fixes are otherwise present: the deadline is wired into the main Validation: unmodified-head Not performed: live-local relay validation under |
|
🤖 Follow-up review at
Verified: full Live-local pass: built the relay, ACP harness, CLI and admin at this exact head; ran an isolated local relay with two deterministic ACP protocol peers, no real LLM. Typing, presence, heartbeat and inactivity timers were disabled.
The queue-overflow path was checked through the package regression and source review, not a live 500-event load test. No real-provider/LLM or CI claim. Local and remote PR heads match; worktree remains clean. My previously reported blockers are cleared; other repository gates still apply. |
Chessing234
left a comment
There was a problem hiding this comment.
hold-deadline select arm is the right fix for a quiet event loop starving ForkAfterHold. tokio Instant switch + retain_held_scopes when the queue is empty looks consistent.
wpfleger96
left a comment
There was a problem hiding this comment.
Reviewed deae68a52cc7667f17e2194eb2a7114bf523b7f4. I found no blocking or non-blocking code issues.
I traced the deadline from the main select! arm through dispatch_pending, including the quiet-loop wake, pool-exhaustion retry, queue-cap eviction cleanup, and forked-session generation fence through worker return and invalidation. The implementation addresses both follow-ups from #7337 without widening the scope.
The exact-head CI run is green. For the fix-PR mutation check, I retained the added tests and reverted the production portions of all three changed files; just test-unit went red while compiling buzz-acp with 33 errors at the removed deadline, queue-pruning, and generation-fence seams. The existing exact-head live-local evidence also covers quiet-loop dispatch, pool-exhaustion recovery, and stale-owner return.
…#7340) ## Summary Adds an independent deadline wakeup so held thread work dispatches after its 10-second bound even when the relay loop is otherwise quiet. Fences session ownership by generation so a worker returning after a fork cannot make an older provider session claimable again. This follows up on the two post-merge findings from [block#7337](block#7337 (review)). ### Related issue Follow-up to block#7337. ### Testing - `cargo test -p buzz-acp` - `cargo clippy -p buzz-acp --all-targets -- -D warnings` - Pre-push file-size, differential Rust test, and desktop Tauri gates No UI changes. --- **Update Sep 4, 15:35:** Addressed both Codex review findings. - Queue-cap eviction now prunes orphaned hold deadlines. - An expired hold stays expired until a worker is successfully claimed. - Hold timers remain disabled while every worker is busy; worker return wakes dispatch directly. - Added regressions for queue eviction and pool exhaustion. Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> Signed-off-by: Trevor P <trev2005@gmail.com>
* fix(mobile): style inline code with the app mono face (block#6631) ## Summary Inline code on mobile renders as **bold body text on a faint background wash** — no monospace face, no chip, and it cannot wrap. block#5257 diagnosed this as a missing `highlightBuilder`. That is no longer the right fix. `gpt_markdown` 1.2.0 deprecates `highlightBuilder` (removal in 2.0.0), renders inline code as a real chip, and adds `InlineCodeStyle` for restyling it. The package author confirmed this on the issue. So this PR is an upgrade — 1.1.6 → 1.2.1 — plus one theme declaration, rather than the builder the issue originally asked for. **Where the style is declared.** `GptMarkdownThemeData` goes in `AppTheme._buildTheme`, which both `light()` and `dark()` call. That reaches all four `GptMarkdown` call sites — `message_content`, `transcript_item_widget`, `token_pill`, `custom_emoji_render` — so the style is stated once instead of per widget. A widget-level `inlineCodeStyle` would have covered channel messages only, leaving the other three on the package's defaults. **What is declared.** Face, size, ink, chip fill and outline — not the face alone. A face name on its own leaves the rest on the package's defaults, which put inline code at 14.1sp beside a fenced block's 13, on a neutral `onSurface` tint rather than the app's code surface. In dark that tint is *lighter* than the surface, while every other code surface in the app is recessed, so the chip read as a different kind of object. All of it now comes from one `CodeStyle` declaration that the fenced block reads from too, so the two cannot be edited apart. **Three adaptations the upgrade requires.** Each was found by running the gate, not by reading the changelog: 1. **`imageBuilder` widened** to `(context, url, width, height)`. This is a hard compile error, and it is **not listed in the package's migration guide**, which states "nothing here stops code compiling". Worth reporting upstream. 2. **`autolink` now defaults to `true`.** `normalizeBareLinks()` already rewrites bare URLs into Markdown links before rendering, so both would run. `message_content` opts out with `autolink: false` to keep current behaviour exactly. The migration guide argues for dropping the pre-processor instead — a better fix, but a behavioural change that belongs in its own PR. 3. **`gpt_markdown.dart` now re-exports `markdown_config.dart`**, making two direct imports redundant. `flutter analyze` reports `No issues found!` on 1.1.6 and flags both on 1.2.1, so these warnings are new, not pre-existing. **Deliberately out of scope.** The three non-message call sites now autolink bare URLs, since only `message_content` has a pre-processor to collide with. Custom inline components (`_MentionMd`, `CustomEmojiMd`, `_ChannelLinkMd`) could additionally declare `allScopesExceptLinkLabel` — 1.2.0 offers it as the fix for a `WidgetSpan` chip going blank inside a link label on iOS — but current behaviour is unchanged without it, so that stays a separate change. ### Related issue Fixes block#5257 Duplicate scan: searched `gpt_markdown`, `inline code mobile`, `highlightBuilder` and `InlineCodeStyle` across both PRs and issues. No open PR touches inline code styling. block#6135 (link labels) and block#6166 (text selection) also touch mobile Markdown but address different defects. ### Testing Full gate, `just ci` — exit 0: | Stage | Result | |---|---| | Rust (33 suites) | 4768 passed, 0 failed | | Desktop | 5799 passed, 0 failed | | Mobile | **2011 passed**, 0 failed | | `flutter analyze` | `No issues found!` | | Desktop + web build | ok | Run on the branch with `main` merged in, so these numbers match what CI builds. **New regression test** — `renders inline code in the app code style`. It resolves the `CodeTextSpan` the package tags inline code with, which carries both the resolved `TextStyle` and the colours the chip behind it is painted with, so face, size, ink, fill and outline are all asserted rather than a widget's presence. It is negative-controlled: reverting only the theme declaration fails it with ```text Expected: a numeric value within <0.001> of <13.0> Actual: <14.1> ``` and dropping the declaration entirely falls back to `packages/gpt_markdown/JetBrainsMono` — so the test measures the real thing, and it would catch a future regression that silently drops the theme extension. The test passes `baseStyle: messageBodyTextStyle`, the style the message surfaces actually use; the widget's own fallback is the smaller `bodyMedium`, which would move the expected size. The test finds paragraphs with `find.byWidgetPredicate((widget) => widget is RichText)`, not `find.byType(RichText)`: inline code renders through `BidiRichText`, a `RichText` subclass, and `byType` matches exact runtime types. That is a hazard for any test that reads text back out of a paragraph, and one landed after this branch was cut: `message_content_custom_emoji_test.dart` arrived with block#6996 and its `code keeps literal emoji while adjacent known tokens render` case reads a code span through `find.byType(RichText)`. It passes on `main` and fails on the merge result, which is what CI builds, so it went red only once CI was authorized. It now uses the same predicate. The two other `byType(RichText)` call sites — the rest of that file and `message_author_meta_test.dart` — were re-run and pass: their content carries no code span, so the exact type still matches. They were left alone. ### Screenshots Rendered through the real `MessageContent` widget with the app's own fonts loaded, at 390pt wide, 3x DPR. Sample text: ``Set `BUZZ_RELAY_URL` before launch, then run `just mobile-test` to verify.`` | | Before (1.1.6) | After (1.2.1) | |---|---|---| | Light |  |  | | Dark |  |  | Before: bold Inter on a flat wash, no chip edge, and `just mobile-test` breaks across the line with the wash simply ending. After: Geist Mono in a bordered, rounded chip, and the wrapped fragment gets its own chip on each line. --------- Signed-off-by: Tolga Cinisli <tolgacinisli@gmail.com> Co-authored-by: Tolga Cinisli <tolgacinisli@gmail.com> * fix(buzz-acp): wake held ACP threads and fence forked sessions (block#7340) ## Summary Adds an independent deadline wakeup so held thread work dispatches after its 10-second bound even when the relay loop is otherwise quiet. Fences session ownership by generation so a worker returning after a fork cannot make an older provider session claimable again. This follows up on the two post-merge findings from [block#7337](block#7337 (review)). ### Related issue Follow-up to block#7337. ### Testing - `cargo test -p buzz-acp` - `cargo clippy -p buzz-acp --all-targets -- -D warnings` - Pre-push file-size, differential Rust test, and desktop Tauri gates No UI changes. --- **Update Sep 4, 15:35:** Addressed both Codex review findings. - Queue-cap eviction now prunes orphaned hold deadlines. - An expired hold stays expired until a worker is successfully claimed. - Hold timers remain disabled while every worker is busy; worker return wakes dispatch directly. - Added regressions for queue eviction and pool exhaustion. Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> --------- Signed-off-by: Tolga Cinisli <tolgacinisli@gmail.com> Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: TolgaCinisli <tolga.cinisli@photier.com> Co-authored-by: Tolga Cinisli <tolgacinisli@gmail.com> Co-authored-by: Salman Mohammed <smohammed@squareup.com>
…-enforcement * origin/main: feat(mesh): upgrade to mesh-llm 0.76.0-rc8 and recommend Qwen3.8 27B (#6189) fix(agent): route GPT-5+ model-service FQNs to Responses (#7358) fix(buzz-acp): wake held ACP threads and fence forked sessions (#7340) fix(mobile): style inline code with the app mono face (#6631) chore(release): release Buzz Desktop version 0.5.23 (#7381) fix(desktop): keep packaged frontendDist relative so Windows embeds assets (#7177) fix(sidebar): simplify unread indicators and emphasize priority activity (#7134) Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
* origin/main: feat(mesh): upgrade to mesh-llm 0.76.0-rc8 and recommend Qwen3.8 27B (#6189) fix(agent): route GPT-5+ model-service FQNs to Responses (#7358) fix(buzz-acp): wake held ACP threads and fence forked sessions (#7340) fix(mobile): style inline code with the app mono face (#6631) chore(release): release Buzz Desktop version 0.5.23 (#7381) fix(desktop): keep packaged frontendDist relative so Windows embeds assets (#7177) fix(sidebar): simplify unread indicators and emphasize priority activity (#7134) Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
* origin/main: (29 commits) fix(acp): pace targeted overflow recovery on consumer capacity (#7325) fix(link-preview): keep composer fetches user-paced (#7211) feat(mesh): upgrade to mesh-llm 0.76.0-rc8 and recommend Qwen3.8 27B (#6189) fix(agent): route GPT-5+ model-service FQNs to Responses (#7358) fix(buzz-acp): wake held ACP threads and fence forked sessions (#7340) fix(mobile): style inline code with the app mono face (#6631) chore(release): release Buzz Desktop version 0.5.23 (#7381) fix(desktop): keep packaged frontendDist relative so Windows embeds assets (#7177) fix(sidebar): simplify unread indicators and emphasize priority activity (#7134) Add generic information-flow control core (#7293) feat(buzz-acp): update base prompt; add buzz context and skills to Pi agents (#7335) fix(desktop): restore mention chip identity icons (#7338) Persist video playback speed preference (#7336) Verify ACP relay events before prompt routing (#7010) fix(buzz-acp): bound busy-owner hold to prevent cross-channel starvation (#7337) feat(desktop): invite owned agents from standalone forums (#7125) fix(desktop): authorize remote mentions at publication (#7124) fix(acp): rename system tag to agent-instructions (#7332) fix(desktop): bind duplicate mention selections to exact recipients (#7133) refactor(relay): extract NIP-29 membership authorization (#7285) ... Signed-off-by: Tom Brow <tomb@block.xyz>
…stody * origin/main: fix(acp): pace targeted overflow recovery on consumer capacity (block#7325) fix(link-preview): keep composer fetches user-paced (block#7211) feat(mesh): upgrade to mesh-llm 0.76.0-rc8 and recommend Qwen3.8 27B (block#6189) fix(agent): route GPT-5+ model-service FQNs to Responses (block#7358) fix(buzz-acp): wake held ACP threads and fence forked sessions (block#7340) fix(mobile): style inline code with the app mono face (block#6631) Signed-off-by: Joel Robotham <jrobotham@squareup.com>
* origin/main: (77 commits) fix(acp): pace targeted overflow recovery on consumer capacity (#7325) fix(link-preview): keep composer fetches user-paced (#7211) feat(mesh): upgrade to mesh-llm 0.76.0-rc8 and recommend Qwen3.8 27B (#6189) fix(agent): route GPT-5+ model-service FQNs to Responses (#7358) fix(buzz-acp): wake held ACP threads and fence forked sessions (#7340) fix(mobile): style inline code with the app mono face (#6631) chore(release): release Buzz Desktop version 0.5.23 (#7381) fix(desktop): keep packaged frontendDist relative so Windows embeds assets (#7177) fix(sidebar): simplify unread indicators and emphasize priority activity (#7134) Add generic information-flow control core (#7293) feat(buzz-acp): update base prompt; add buzz context and skills to Pi agents (#7335) fix(desktop): restore mention chip identity icons (#7338) Persist video playback speed preference (#7336) Verify ACP relay events before prompt routing (#7010) fix(buzz-acp): bound busy-owner hold to prevent cross-channel starvation (#7337) feat(desktop): invite owned agents from standalone forums (#7125) fix(desktop): authorize remote mentions at publication (#7124) fix(acp): rename system tag to agent-instructions (#7332) fix(desktop): bind duplicate mention selections to exact recipients (#7133) refactor(relay): extract NIP-29 membership authorization (#7285) ... Signed-off-by: Sol <478bb5a31222ea2b28a3d1afb8b1d598940628f19c2a87efc3c4b822299eeec6@buzz.block.builderlab.xyz> # Conflicts: # desktop/src-tauri/src/commands/media_download.rs # desktop/src-tauri/src/lib.rs
Brings in 14 upstream commits: the npub identity standardisation for mobile and desktop (block#7488–block#7503, block#7493, block#7494), inline code in the app mono face via gpt_markdown 1.2.1 (block#6631), ACP fixes (block#7340, block#7325, block#7538), link-preview pacing (block#7211), mesh-llm 0.76.0-rc8 (block#6189) and the Codex Astra adapter gate (block#7427). Conflicts (4 files, 5 hunks) resolved as follows: - compose_bar/suggestions.dart: keep the fork's _RevealWhenSelected row and selection highlight, take upstream's `candidate.initial` for the avatar fallback so an unnamed candidate's compact npub does not render `N` for everyone. - message_content.dart: keep the fork's _buildMedia (ref + appContentAvailable) under gpt_markdown's four-argument imageBuilder; the `=WxH` size hint is ignored since media here is sized from imeta. Keep the fork's fenced code block (SelectionContainer.disabled, language label, Copy, horizontal scroll) and take upstream's CodeStyle colours so fenced and inline code share one face. `autolink: false` merged cleanly and is required: normalizeBareLinks() already links bare URLs. - invites/invite_create_provider.dart: keep both imports; upstream's shortPubkey replaces the local invite helpers. - test/.../channels_page_test.dart: keep the fork's navigatorObservers and take upstream's `profile` parameter. `tabReselection` is dropped — the fork removed it in eb1eefa when the tab bar became sidebar rows. Auto-merged files that the fork also edits were checked by hand: search_page and channel_tile took upstream's `user.initial` / dmAvatarInitial, forum and note cards lost their local _shortPubkey, and app.dart wraps the builder in AppMarkdownTheme. Verified locally: dart format, flutter analyze, flutter test (2,504), cargo fmt --check, just test-unit, file-size-check, BuzzPushKit compiles. Swift tests and device checks run on the Intel Mac. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G7tN8KwTRSSrHe2PSmnnM3 Signed-off-by: dhseo <birdblues@mac.com>
Summary
Adds an independent deadline wakeup so held thread work dispatches after its 10-second bound even when the relay loop is otherwise quiet. Fences session ownership by generation so a worker returning after a fork cannot make an older provider session claimable again.
This follows up on the two post-merge findings from #7337.
Related issue
Follow-up to #7337.
Testing
cargo test -p buzz-acpcargo clippy -p buzz-acp --all-targets -- -D warningsNo UI changes.
Update Sep 4, 15:35: Addressed both Codex review findings.
Generated with Codex