Repository navigation
Conversation
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 Two reproducible issues in recipient selection and native-read waiting.
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent systems/lifecycle, correctness/security and product/accessibility passes.
One low-priority finding is recorded inline. The stale-display/admission separation and launch-time wait paths were reviewed against their callers and lifecycle guards.
Reviewed head fa94a50 against base a65463a. Source-only: no tests, builds, app execution or independent CI assessment. Native DM creation, real-relay behavior, performance, rendered focus and assistive-technology behavior remain unverified. Inspected the public description, commit messages, diagram and one-second video frame samples; the fixture recordings show an earlier revision and do not establish current-head runtime correctness.
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, integrating independent systems/lifecycle, correctness/security and product/accessibility passes.
No new actionable findings in the rebase integration. The existing P3 recipient-picker loading-status finding remains unresolved at this head: the native-list wait can still leave an empty picker without loading feedback. Please retain that existing finding; no duplicate inline comment is added.
Compared the earlier reviewed fa94a50 with this revision: four patches are unchanged; the first adapts to the current name-fallback and reconnect code. Checked retained library rows, freshness/admission separation, native-list joins, cancellation and error/retry callers.
Reviewed head 0c8eb04 against base 4bcd920 (merge base 561265d). Source-only: no tests, builds or app execution. Hosted checks were still running in the single snapshot. Native DM creation, real-relay recovery, performance, rendered focus and assistive-technology behavior remain unverified. Inspected the public description, five commit messages, diagram and sampled frames from all four unchanged fixture videos; those recordings show an earlier revision and do not establish current-head runtime correctness.
…perseding native lists Addresses PR #1909 review comments. Recipient picker: while the native agent list loaded, the picker listed and accepted every directory agent, so one the viewer does not control could be chosen and was only refused at Send. It now keeps offering only controlled agents throughout. While the list loads it still holds Space and withholds "No matching people.", and now shows its existing "Loading people" status for that wait too, so the list is never blank without feedback (DESIGN.md, "States, feedback, and motion"). Controlled agents appear once the list arrives. Native list joins: a refresh that joined list A resolved when A finished even if a later list B, started by a change such as a creation, had superseded it and A's result was discarded. A New message send that joined the launch list could then refuse while the list was still loading. `AgentsService.refresh` now has a superseded read's waiters follow the read that replaced it, as `library.refresh` does, so a finished refresh always means the current list. Tests: the picker test now asserts that only controlled agents are listed while the native list loads, with the loading status in place of an empty result. A service test asserts that a joined refresh waits through a superseding read. Both fail on the previous sources: the picker test on each change separately, the service test because the joined waiter resolved early. The 24 related test files, typecheck, biome and design:check pass. Production diff: +11/-15 (net -4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, integrating independent systems/lifecycle, correctness/security and product/accessibility passes.
No new actionable findings. The prior missing loading-status finding is repaired: the picker announces loading while the native agent list is pending. The follow-up also keeps uncontrolled agents out of selection and makes joined refresh callers wait for superseding native reads. Checked the five-file delta from 0c8eb04, its regression-test source, and the affected callers and lifecycle guards.
Reviewed head 5831134 against base 4bcd920 (merge base 561265d). Source-only: no tests, builds, app execution or CI assessment. Native DM creation, real-relay behavior, performance, rendered focus and assistive-technology behavior remain unverified. Inspected the public description, six commit messages, diagram and sampled frames from all four unchanged fixture videos; unsampled footage was not reviewed, and those earlier-revision recordings do not establish current-head runtime correctness.
…perseding native lists Addresses PR #1909 review comments. Recipient picker: while the native agent list loaded, the picker listed and accepted every directory agent, so one the viewer does not control could be chosen and was only refused at Send. It now keeps offering only controlled agents throughout. While the list loads it still holds Space and withholds "No matching people.", and now shows its existing "Loading people" status for that wait too, so the list is never blank without feedback (DESIGN.md, "States, feedback, and motion"). Controlled agents appear once the list arrives. Native list joins: a refresh that joined list A resolved when A finished even if a later list B, started by a change such as a creation, had superseded it and A's result was discarded. A New message send that joined the launch list could then refuse while the list was still loading. `AgentsService.refresh` now has a superseded read's waiters follow the read that replaced it, as `library.refresh` does, so a finished refresh always means the current list. Tests: the picker test now asserts that only controlled agents are listed while the native list loads, with the loading status in place of an empty result. A service test asserts that a joined refresh waits through a superseding read. Both fail on the previous sources: the picker test on each change separately, the service test because the joined waiter resolved early. The 24 related test files, typecheck, biome and design:check pass. Production diff: +11/-15 (net -4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated follow-up source review via Wes’s account, integrating independent Rocket, Mantis and Groot review lanes.
No new actionable findings. The prior recipient-filtering, loading-status and superseding-native-read fixes remain intact. Reviewed the rebase integration with hosted-agent canMessageAgent eligibility, existing-DM reopening, refresh/retry and cancellation boundaries. No changes requested from this source review.
Reviewed head 36397014e43a29ecf2d8a2212cb9e63bf76e6490, base e7ae7b18cff03f371382ae60fc1064b60a667ed2 (merge-base ca836a3109be30a7fd46db97bdcceaf32ebabf65). Source-only: no tests, builds or app execution. One hosted-check snapshot showed 39 passed and two skipped (Windows validation and flake check); this does not establish native/real-relay behavior, performance, rendered focus or assistive-technology behavior. The public description, six commit messages, diagram and 16 sampled frames across all four videos were inspected; unsampled footage remains unreviewed, and those earlier fixture recordings do not validate this head.
…perseding native lists Addresses PR #1909 review comments. Recipient picker: while the native agent list loaded, the picker listed and accepted every directory agent, so one the viewer does not control could be chosen and was only refused at Send. It now keeps offering only controlled agents throughout. While the list loads it still holds Space and withholds "No matching people.", and now shows its existing "Loading people" status for that wait too, so the list is never blank without feedback (DESIGN.md, "States, feedback, and motion"). Controlled agents appear once the list arrives. Native list joins: a refresh that joined list A resolved when A finished even if a later list B, started by a change such as a creation, had superseded it and A's result was discarded. A New message send that joined the launch list could then refuse while the list was still loading. `AgentsService.refresh` now has a superseded read's waiters follow the read that replaced it, as `library.refresh` does, so a finished refresh always means the current list. Tests: the picker test now asserts that only controlled agents are listed while the native list loads, with the loading status in place of an empty result. A service test asserts that a joined refresh waits through a superseding read. Both fail on the previous sources: the picker test on each change separately, the service test because the joined waiter resolved early. The 24 related test files, typecheck, biome and design:check pass. Production diff: +11/-15 (net -4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, integrating independent Rocket, Mantis and Groot review lanes.
No new actionable findings in the rebase integration. The prior recipient-filtering, loading-status and superseding-native-read fixes remain intact. Compared all six feature patches with previously reviewed 36397014e43a29ecf2d8a2212cb9e63bf76e6490, including the short-query picker state, keyed search cancellation, native refresh bridge and retained-display/admission boundaries. No changes requested from this source review.
Reviewed head 823454777007257fbc374827e8fd68b06f3c71e6 against base 90ce5e37a851909cc0e5fd4da2f469010e4eb7bf. Source-only: no tests, builds or app execution. One hosted-check snapshot showed 39 successful checks and two skipped (Windows native validation and flake check); native/real-relay behavior, performance, rendered focus and assistive-technology behavior remain unverified. Inspected the public description, six commit messages and attached diagram. The four historical fixture videos were not viewed in this cycle and do not establish current-head runtime correctness.
## TL;DR Solo chats retain explicit model and effort choices per chat and agent until Reset, including choices made before the first message. Untouched chats keep provider defaults. Choices use a feature-owned IndexedDB transaction for read/merge/compare/write. The existing composer catalog, menus and scoped draft state remain their owners; no generic storage framework or second authoritative localStorage copy is added. IndexedDB is the sole authority: a missing row means no saved choice. This unreleased feature does not import earlier development-only localStorage bytes; those bytes remain untouched and non-authoritative. Scope cleanup shares the save/transfer ownership lock so an already-queued save cannot recreate deleted choices. Other windows are notified only after commit and reread the authoritative store. Send waits for initial loading and pending-save acknowledgement. Failed reads offer **Retry saved choices** rather than silently using defaults; failed saves keep local intent. First-send transfer and refused-start restoration use ownership receipts, so a later edit or equal-value Reset is not overwritten. The existing toast retains the refused prompt, attachments and validated choices for **Retry restoring message**, including repeated ownership/storage failure. ## Verification Blox verified an uploaded content snapshot matching `6f87b1101d21f3071cf0a64ab0197ae1442a4429`: typecheck, targeted lint, **622 tests across the three affected full files**, and **14 browser cases** across Chromium/WebKit (zero skipped/flaky). All six final changed-file hashes match the tested snapshot. Historical `a004b3cd` verified 709 tests across seven files plus design checks; those broader results are not claimed as a new-head run. These are content-snapshot checks, not claims that the candidate commit was checked out remotely. Pre-commit hooks ran; only duplicate types/tests/design/clippy pre-push jobs were skipped because required CI runs them. The verification box was deleted. The latest feedback regressions fail against `a004b3cd` and pass with these fixes: a refused prompt-write reports the text-save cause, compensates prepared choices, and succeeds through Retry; fresh IDB initialization remains available when the unrelated localStorage read throws; and scope purge waits behind already-queued saves in both browser engines. The [failed WebKit job](https://github.com/block/buzz-app/actions/runs/41872987261/job/126725503435) was not dismissed as a flake: an owned amd64 trace showed the second Web Lock holder reading stale localStorage after the first writer committed, then replacing the durable map. The transactional repair preserves both agents in the unchanged two-page regression. Mounted tests cover pending/failed edits, Send commit barriers, unavailable types, navigation and refused-start recovery. The latest regressions preserve corrective edit errors during a saved-choice read failure and retain valid first-send choices when the catalog needs no selector. Both fail with their previous production behavior and pass with the fixes. Removing the later-Reset ownership guard produces the intended regression failure; the restored source passes. **Browser cases: four added, none removed** versus the merge base; this feedback batch adds one and updates one. They cover keyboard/Escape/Reset/focus, actual cross-page commits/notifications, real IDB initialization/abort/first-send compare-clear custody, and Web Lock queue ordering for purge. These browser boundaries cannot be established by jsdom; lifecycle and failure permutations remain in mounted tests. Shared menus, Button, Toast and LoadingRegion are reused without new styling. Prior picker checks covered both themes, 360/1280px widths, normal/24px text, keyboard and pointer: [before selection](https://d24qwcpro867f5.cloudfront.net/repos/block/buzz-app/prs/2342/solo-choices-before-03b33b1.png), [selected choices](https://d24qwcpro867f5.cloudfront.net/repos/block/buzz-app/prs/2342/solo-choices-selected-03b33b1.png). **Boundaries:** Cooperation is within the same storage bucket, not older clients or separate profiles. Retry retention ends when this window reloads or its notice is dismissed; it is not crash recovery. Native simultaneous WKWebView/provider execution and full-chat runtime proof remain unverified. Earlier failed attempts and test-contamination evidence remain retained; passing repetitions do not establish that every browser failure is fixed. The separate JavaScript fixture failure was repaired on main by [#2444](#2444); this branch includes that merged source, not an unmerged human patch. ## Related Fixes NEXUS-4155 This is native host Solo composer behavior, separate from plugin task setup and shared-channel default routing. It does not depend on [#2306](#2306) or [#1909](#1909). Part of [Nexus & Agent Tooling status](https://g2.sqprod.co/apps/kylez-nexus-buzz-plugins-status). _Generated with Codex (gpt-6.1-sol, high) on behalf of kylez._ Signed-off-by: Kyle Zemek <kylez@squareup.com> Co-authored-by: Codex <noreply@openai.com>
…perseding native lists Addresses PR #1909 review comments. Recipient picker: while the native agent list loaded, the picker listed and accepted every directory agent, so one the viewer does not control could be chosen and was only refused at Send. It now keeps offering only controlled agents throughout. While the list loads it still holds Space and withholds "No matching people.", and now shows its existing "Loading people" status for that wait too, so the list is never blank without feedback (DESIGN.md, "States, feedback, and motion"). Controlled agents appear once the list arrives. Native list joins: a refresh that joined list A resolved when A finished even if a later list B, started by a change such as a creation, had superseded it and A's result was discarded. A New message send that joined the launch list could then refuse while the list was still loading. `AgentsService.refresh` now has a superseded read's waiters follow the read that replaced it, as `library.refresh` does, so a finished refresh always means the current list. Tests: the picker test now asserts that only controlled agents are listed while the native list loads, with the loading status in place of an empty result. A service test asserts that a joined refresh waits through a superseding read. Both fail on the previous sources: the picker test on each change separately, the service test because the joined waiter resolved early. The 24 related test files, typecheck, biome and design:check pass. Production diff: +11/-15 (net -4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, integrating independent Rocket, Mantis and Groot review lanes.
No new actionable findings in the rebase integration. The prior recipient-filtering, loading-status and superseding-native-read fixes remain intact. Compared the six feature patches with previously reviewed 823454777007257fbc374827e8fd68b06f3c71e6, focusing on verified-name recipient selection, existing-DM visibility, remote-agent labels, native-list lifecycle and Solo/channel admission. The documented distinction between hosted DM eligibility and channel-admission evidence is preserved. No changes requested from this source review.
Reviewed head 1438e6593a3f375a41b339bfd2ca251f5517f77c against base fe3dd1947181de66dc6e4fd8ca5d3b61573c0ca2 (merge-base 1c3047558547e76bd006c2dc76951b3f1d601c5d). Source-only: no tests, builds, app execution or CI assessment. Native/real-relay behavior, performance, rendered focus and assistive-technology behavior remain unverified. Inspected the public description, six feature commit messages and attached diagram. The four historical fixture videos were not viewed in this cycle and do not validate this head.
…uzz tools (#2707) ## TL;DR An approved external app (like Nexus) now gets the selected agent's whole Buzz MCP tool catalog and native dispatcher, including tools plugins add later, after one explicit "full access" approval. Before, it got a hand-picked list of six tools with rewritten arguments. Older narrow approvals still connect but grant no tools until the owner approves full access again, so no existing grant widens on its own. Initiative status: https://g2.sqprod.co/apps/kylez-nexus-buzz-plugins-status Fixes NEXUS-4262 ## What changes - **One scope, `agent`.** New links request `scope=agent`, and `agent` can't be combined with other scopes. Rust pairing accepts only `["agent"]` for new approvals (`valid_scopes`). Saved `read`/`post`/`create` approvals still load, renew and resume, but grant nothing. - **No adapter.** `respondExternal` now passes `tools/list` and `tools/call` to `respond` in `rpc.ts`, using the same `sessionTools(added)` catalog as native sessions. Plugin-added tools come from `addedTools.snapshot()` (the source native harnesses use) and are re-read on every message, so enabling or disabling a plugin changes the catalog live. The old policy map, argument rewriting and Bestie injection are removed. - **The agent's own authority.** The connection uses the agent's run handle (`runners.handle(key, undefined, true)`), so it gets device settings, plugin requests, Git access and handoff exactly as a native run does. Plugin installs still go through the owner's confirmation. Memory reads use the agent's `memoryListing`, like the native plugin path. Protected agents are still refused. `plugin_disable` acts at once, as it does natively, and the consent text says so. - **Handoff with true provenance.** `self_handoff` goes through the run's own handoff and `Agents.wakeIn`, but its `from` is `{ external: true }`. `wakeSection` shows it as an `<app-wake>` section that says an external app the owner approved sent it, and that it is not a message, a watch or timer, or another of the agent's conversations. - **Files where the agent reads them.** `send` with `path`/`files` calls a new optional `AgentType.readFile`. Claude, Codex, Pi and goose implement it with the reader their own tools use, in the agent's saved workspace on this device. Relative paths start there; absolute and `~/` paths reach any file the account can read, as they do natively, and the consent text says so. A Claude or Codex agent bound to an execution target refuses, because only a conversation's session opens that target. A type without `readFile` refuses with the same message its tools give natively. Closing the connection cancels a read in progress. - **No implicit conversation.** Calls run with an empty context, so every tool needs explicit channel or message IDs. - **Consent.** The dialog now states full access plainly: future plugin tools, Git access, waking the agent, Nexus workers, settings changes, posting any file the agent can read (including outside its workspace), and immediate plugin disabling. A legacy narrow link shows an update-required notice with Deny only, which sends the normal denial callback. `approve()` refuses non-`agent` requests as a second check. Agent settings flags a saved legacy approval as no longer working. ## Companion-client contract (for the Nexus consumer) | | Behavior | | --- | --- | | Request | `scope=agent` alone. Narrow scopes get the update-required notice and only a denial callback. | | `tools/list` | The full native catalog: 66 built-in tools with settings, plus plugin tools. Each tool is listed as natively, plus `_meta: {"buzz/requiredScopes": ["agent"]}`. Result `_meta`: `buzz/grantedScopes` (`["agent"]`, or `[]` for a legacy approval) and `buzz/supportedScopes: ["agent"]`. Cursors are rejected (single page). | | `tools/call` without the grant | `isError` result, nothing dispatched, `_meta["buzz/error"] = {code: "authorization_required", required_scopes: ["agent"]}`. | | `tools/call` with the grant | Native result and errors, unchanged. | | `self_handoff` | Native arguments. The wake reaches the agent as an `<app-wake>` from the app. | | `send` `path`/`files` | Read by the agent's type in its saved workspace. Agents on an execution target, and types without a reader, return their refusal. | | `channel_create` | Takes native arguments. A private Solo chat with the owner's Bestie must now pass `visibility: "private"`, `solo: true` and `members` explicitly. Buzz no longer fills them in. | An unupdated consumer that accepts only `read`/`post`/`create` will reject `supportedScopes: ["agent"]`. Its upgrade path is to accept `agent` and request `scope=agent`, which takes one owner approval. ## Overlap These open PRs touch nearby files. This PR neither depends on nor replaces any of them: - #2260 and #2685 cover external pairing and identity. - #2647 adds plugin skills to `session-tools`. - #2622 covers Bestie settings. - #1939, #1909, #1854, #2311 and #2070 change `service.ts`/`runner.ts`. - #2610 has no shared files. ## Validation Ran on a Linux Blox box against the current head: - `tsc --noEmit` and `tsc -p tsconfig.design.json`; - Biome on the touched files; - Vitest for `src/buzz-mcp`, the external connection, dialog and settings tests, `AgentsPage.test.tsx`, `service.test.ts`, `plugin-wake.test.ts` and the Claude, Codex, Pi and goose runtime tests. New tests cover the external handoff's trigger and `<app-wake>` wording, file reads through the type's reader (target workspace, cancellation on close, native refusal), the Claude and Codex workspace and execution-target cases, a saved narrow approval that a link asks to resume as `agent`, and memory read from the selected agent. The Rust pairing tests (`host_mcp::pairing`) need the Tauri system libraries, so they are left to PR CI. No browser journeys were added or removed. Independent security reviews traced authorize, approve, resume, renew and call, and found no path that grants tools without the `agent` scope. Not done: - The native desktop consent dialog and a live external connection have not been exercised against this build, including a live `<app-wake>` turn and a file read in a running Claude, Codex, Pi or goose agent. - No human testing yet. - The Nexus side of the contract is a separate change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Kyle Zemek <kylez@squareup.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…perseding native lists Addresses PR #1909 review comments. Recipient picker: while the native agent list loaded, the picker listed and accepted every directory agent, so one the viewer does not control could be chosen and was only refused at Send. It now keeps offering only controlled agents throughout. While the list loads it still holds Space and withholds "No matching people.", and now shows its existing "Loading people" status for that wait too, so the list is never blank without feedback (DESIGN.md, "States, feedback, and motion"). Controlled agents appear once the list arrives. Native list joins: a refresh that joined list A resolved when A finished even if a later list B, started by a change such as a creation, had superseded it and A's result was discarded. A New message send that joined the launch list could then refuse while the list was still loading. `AgentsService.refresh` now has a superseded read's waiters follow the read that replaced it, as `library.refresh` does, so a finished refresh always means the current list. Tests: the picker test now asserts that only controlled agents are listed while the native list loads, with the loading status in place of an empty result. A service test asserts that a joined refresh waits through a superseding read. Both fail on the previous sources: the picker test on each change separately, the service test because the joined waiter resolved early. The 24 related test files, typecheck, biome and design:check pass. Production diff: +11/-15 (net -4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord automated source review via Wes’s account, integrating independent Rocket, Mantis and Groot review lanes.
No new actionable findings in this focused rebase follow-up at 4a0e3d4f85f4a782f90f69f0293fd6d02a0f0d20, base/merge-base cae3922574c5c1f0ce5d32231810229e94b1bb40, compared with reviewed 1438e6593a3f375a41b339bfd2ca251f5517f77c.
The six feature patches retain the reviewed behavior: five are patch-identical and the first differs only in documentation context. We checked their integration with the changed native-agent service, relay recovery, DM/picker and Solo/composer consumers. Retained library rows remain display evidence, admission still requires managed-or-ready-library evidence, and native waits preserve joining/supersession and cancellation. The prior picker filtering/loading and superseding-read repairs remain intact; their regression coverage was inspected as source.
This was a focused integration review, not a full review of unrelated changes in the new base. No tests, builds or app execution were performed. Native/real-relay behavior, performance, rendering, focus and assistive-technology behavior remain unverified. I inspected the current PR description and attached diagram; the four historical fixture videos were not reviewed in this round and do not establish current-head validation.
|
🤖 Design review: no design blockers found at Reviewed source, the diagram and sampled frames from all four attached fixture recordings. Those recordings show an older revision ( |
…ng agents The relay agent library republished `loading`, `error` and, on a live disconnect, an emptied `idle` snapshot, while every consumer admitted its rows only when `ready`. Library-only agents blinked out of mentions, the Solo default-agent chip, typing/working labels, agent icons and alias names on each refresh, stayed hidden after a failed read, and vanished for a whole outage. Separately, New message, search and the agent profile's Message button read `agentChoices.snapshot()` once and refused an agent the native inventory had not listed yet at launch, and the recipient picker hid it as uncontrolled. Library (half A): - `choices.ts` and the name directory carry the last library read through loading, error and stale states; `pending`/`complete` still mark it partial, so inference and templates keep refusing until a ready read. - A live disconnect marks the library stale (rows kept, `idle`, so reconnect rereads a retained library) instead of clearing it; sign-out, cache clear and dispose still empty it. - Channel-agent admission still requires a ready library read for library-only agents; stale rows are display-only. Consumers (half B): - `NewMessage.validateAgents`, the search person action and `AgentMessageButton` await `agentChoices.refresh()` before refusing an agent that is missing while `pending`, as SoloDraft already does. - `RecipientPicker` keeps directory agents listed, holds Space and withholds "No matching people." while agents are pending. docs/agents.md now describes stale-but-usable library rows, the wait-before-refusing rule and stale-on-disconnect. Production diff: +99/-66 across 8 files (net +33; the name-directory change is mostly reindentation). New tests fail on the previous sources and pass here; typecheck and lint pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…y before refusing admission Follow-up to b2fce98. The DM paths gated on the combined `pending`, which is mostly library state, yet only native rows can make an agent `managed`: - `RecipientPicker` listed uncontrolled directory agents, held Space and hid "No matching people." during every routine library refresh, and for a whole outage after a disconnect left the library stale-idle. - New message, the search person action and `AgentMessageButton` waited on a library read (up to ~10 s offline) whose result could not change the decision. `AgentChoicesSnapshot` gains `managedPending` (native list loading). The picker gates on it; the three await paths gate on it and call `agentChoices.refresh(false)`, so they never wait on the relay library. `NewMessage.validateAgents` now takes the send's `controller.signal`. Channel admission keeps its ready-library gate for library-only agents, but `addAgents` no longer refuses a listed agent just because the library is loading, failed or stale: when a key is unknown it awaits one `agentChoices.refresh()` (only if the library is not ready, joining a read in flight) and re-checks before refusing. Archive state is checked after that read. Tests: admission refuses a library-only agent from loading, error and stale-idle libraries when the reread fails and admits it when it succeeds, with one read (joined when in flight); the picker keeps uncontrolled agents hidden while only the library loads; the await paths call `refresh(false)` and do not wait on library-only pending. The new and changed tests fail on b2fce98's sources (admission also fails with the gate removed). Typecheck, lint and format pass. Production diff: +44/-21 (net +23) here; +127/-71 (net +56) with b2fce98. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…nt list Follow-up to 6cc276c, closing the cheap residual risks of keeping library rows through refreshes. - `AgentChoicesSnapshot.admissible` is `selectable` limited to `managed` agents unless the library is ready: the rule session admission already applied inline, now shared. Session admission, SoloDraft start and the composer's tagged-send pre-check use it. - SoloDraft start no longer creates a Solo chat with a stale or failed-read library row: such a member takes the existing refresh path and is refused if the reread does not renew it (R1). The default-agent `picked` stays on `selectable`; it is display only and start re-checks. - A tagged send whose own reread leaves the library unready refuses there instead of `addAgents` reading again, halving the offline wait (R2). - `AgentsService.refresh({ join })` returns a native list already in flight. The `LocalAgents` bridge joins unless `fresh`, matching `library.refresh({ fresh })`, so a DM click during launch waits on the launch read rather than discarding it (R8). Internal post-mutation refreshes still start a new list. Tests: SoloDraft rereads a stale row and refuses or starts on the result; the composer refuses after one library read without calling `addAgents`; choices exposes `admissible` (empty for stale/error/loading library rows, kept for managed agents) and passes `fresh` to native; AgentsService joins the launch read. All fail on 6cc276c's sources. The slow choices.test.ts poll uses `{ interval: 0 }`. Typecheck, biome lint and format pass; 43 test files that build agent choices pass. Production diff: +43/-26 (net +17) here; +175/-92 (net +83) with b2fce98 and 6cc276c. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to 0114650's review. The `LocalAgents` bridge in `createServices` maps `refresh({ fresh })` to `AgentsService.refresh({ join: !fresh })`, but no test tied the two together: mistyping it as `join: fresh` would let post-setup, removal and delete reads join a list already in flight that may predate the change, and every test would still pass. `services.test.ts` now captures the real bridge from `createLocalAgents` and asserts `fresh: true` reaches Agents as `join: false` and the default as `join: true`. It fails with the mapping inverted. SoloDraft's local admission helper is renamed from `selectable` to `admissible`, matching the snapshot field it reads, so it is not mistaken for `snapshot().selectable`, which the default-agent chip still uses on purpose. No behavior change. Touched tests (services.test.ts, src/features/solo) pass; typecheck, lint and format pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to b2fce98, which keeps relay agent library rows through a live disconnect (stale, `idle`) instead of clearing them. ProfilePanel's provider-recovery test still expected the heading to fall back to the public profile name ("Public Carl") once the relay dropped, so the pre-push unit job failed. The heading now stays "Configured Carl" after the drop; the later "Public Carl" assertion, after the provider is disabled, is unchanged. ProfilePanel.test.tsx passes. In the full Vitest run (802 files) no other test pinned the old clear-on-disconnect display. Four unrelated files (project-git, vite-config, claude-code usage, codex-sh) timed out under full-suite load and pass when rerun alone. Typecheck and biome pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…perseding native lists Addresses PR #1909 review comments. Recipient picker: while the native agent list loaded, the picker listed and accepted every directory agent, so one the viewer does not control could be chosen and was only refused at Send. It now keeps offering only controlled agents throughout. While the list loads it still holds Space and withholds "No matching people.", and now shows its existing "Loading people" status for that wait too, so the list is never blank without feedback (DESIGN.md, "States, feedback, and motion"). Controlled agents appear once the list arrives. Native list joins: a refresh that joined list A resolved when A finished even if a later list B, started by a change such as a creation, had superseded it and A's result was discarded. A New message send that joined the launch list could then refuse while the list was still loading. `AgentsService.refresh` now has a superseded read's waiters follow the read that replaced it, as `library.refresh` does, so a finished refresh always means the current list. Tests: the picker test now asserts that only controlled agents are listed while the native list loads, with the loading status in place of an empty result. A service test asserts that a joined refresh waits through a superseding read. Both fail on the previous sources: the picker test on each change separately, the service test because the joined waiter resolved early. The 24 related test files, typecheck, biome and design:check pass. Production diff: +11/-15 (net -4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, integrating independent Rocket, Mantis and Groot review lanes.
No new actionable findings at f1c3cfe773d2174e24f990078b59499d35076294, base/merge-base c9239f737f4985979eb5939e84c33ab3da2b1321, compared with reviewed 4a0e3d4f85f4a782f90f69f0293fd6d02a0f0d20.
Five feature patches are unchanged; the first adapts to the base’s partial-library recovery contract. We checked retained display rows versus admission, reconnect failure recovery and generation fencing, native-list joining/supersession, and the changed picker/retry consumers. The prior recipient filtering, loading-status and superseding-read repairs remain intact. Regression tests were inspected as source.
This is a focused integration review; unrelated changes in the new base were not fully reviewed. No tests, builds, app execution or CI assessment were performed. Native/real-relay behavior, performance, rendering, focus and assistive-technology behavior remain unverified. The current public description and attached diagram were inspected; the four historical fixture videos were not viewed this round and do not validate this head.
Problem
Why it was built this way
Git history shows no security or ghost-agent reason for either behavior:
clear()came from the initial import (Import plugin core #2) as a read fence. Fix startup inventory duplication and late panel scroll shifts #160 kept it so that reconnecting would reread the library.ready.agentChoicesprojection, which turned the admission gate into a display gate.Archives already moved to
stale()in #1665. The library was the last store at that call site that still emptied on disconnect.Fix
loadinganderror.pending/completestill mark the read as partial, so inference and templates keep refusing until a ready read.stale()replacesclear(). In-flight reads are still aborted, and reconnecting still rereads. Sign-out, cache clear and dispose still empty the library.admissiblemeans managed, or the library read is ready. ChanneladdAgents, Solo start and tagged sends use it, so stale rows are display-only.addAgentsrereads once before refusing an unknown key.managedPendingflag. The three that act on a click awaitrefresh(false), which joins the native list already in flight and never waits on the relay library.docs/agents.mdis updated.Net +83 production lines; no new timers or abstractions.
Before / after
Video evidence
All four takes are side by side: BEFORE (
a3a6223d7) on the left, AFTER on the right, with the clocks synchronized. They were recorded in the Playwright browser fixture with library or native reads held for exactly 2 s.A1, Refresh member data. Watch the message author: BEFORE shows a raw key with no agent icon for 2 s, while AFTER keeps "Owned Honey".
A1.mp4
A2, relay disconnect. Watch the author: BEFORE drops it from the disconnect until after "Reconnected", while AFTER keeps it and still offers it in
@Honwhile offline.A2.mp4
B1, sending a restored New message draft during launch. Watch the line above the composer: BEFORE refuses at once and stays refused, while AFTER waits and creates the DM once the list lands.
B1.mp4
B2, the recipient picker during launch. Watch under "To: Owned": BEFORE shows "No matching people." for about 1 s, while AFTER lists the agent as soon as people load.
B2.mp4
Cost / risk
addAgentsrefusal of a library-only agent now takes 3.5–10 s (one reread) instead of being instant.admissible. All three admission paths I know of (channel add, Solo start, tagged send) use it and are tested.Testing
admissible; joining the in-flight native list; and the local-agents bridge mapping.ProfilePanel.test.tsxnow expects the library-configured name ("Configured Carl") to stay through a relay drop instead of falling back to the public name; it fails on the parent's clear-on-disconnect.🤖 Generated with Claude Code