Repository navigation
Conversation
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 One failure/recovery case remains in the new namesake exclusion.
21dfe47 to
3ee7539
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head 3ee75393a69490361dc55ce3d760f49859322021 against base/merge-base 744278296c255c398b38e8e8fc13df445aa9f8ba. Two findings are attached inline: a P2 where the new cold-library flow hides both same-named agents and leaves an exact typed mention untagged, and a narrower P3 where a cached alias bypasses the loading hold. The P3 is a lower-priority follow-up, not an additional readiness blocker.
The review covered the shared chooser/resolver precedence, full-set ambiguity and longer names, agent-library lifecycle/retry, archive and channel admission, notification/import intent, and regression-test source. This was source-only: no tests, builds, app runs, live interaction checks or CI verification. The PR’s historical validation claims were not rerun. The public PR description was inspected; its attached demo video was not viewed, so the video’s visual/privacy surface remains unverified.
3ee7539 to
f0557dc
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head f0557dc615e139eeb4ab34a77fbf5005f44d7299 against base fb9cd7cf45c6d80368cc79a7c34535f8f3b2fd20, retaining the earlier review’s unchanged coverage. Both previous findings are addressed in source; no new actionable findings in this focused follow-up.
- The P2 late-local-row case now appends the offered agent before newly released elsewhere/directory results, preserving shown rows and the result limit. The added composer cases check the local Space target, pending resolution and sent key.
- The lower-priority P3 alias case now holds queries matching cached profile, verified and local-agent aliases in both chooser and resolver. Added cases cover directory-first arrival, Space/Enter selection and pending resolution.
The follow-up also checked failure/retry behavior and the rebase’s naming/cache integration. Source-only: no tests, builds, app runs, CI verification, visual checks or performance measurements. Author-reported validation was not rerun. The public PR description was inspected; its historical demo video remains unviewed, including its visual/privacy surface. This COMMENT is not runtime validation or merge approval.
f0557dc to
bdb4d9b
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source integration follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head bdb4d9bf483247f9eb686a8f6c0600d23adf9173, live base 3bcf1c4bb1111d924a84d9b710c1da2db80ccf0b, merge-base fd21dba4af2edf63d7b038e3f0f6bdf3703b5df7. Prior unchanged coverage from review 5798537429 is retained; both earlier late-local-row and cached-alias repairs remain intact.
One new P2 is attached inline: exact-public-key mentions of an owned agent elsewhere can return to prose before a successful cold library read completes. This follow-up checked the retained feature patches against the inherited exact-key ranking, team-name rules, directory cache/lookup and shared chooser/resolver lifecycle.
Source-only: no tests, builds, app runs, CI verification, visual checks or performance measurements. Regression-test source and the public PR description were inspected; historical validation claims were not rerun. The historical demo video remains unviewed, including its visual/privacy surface.
bdb4d9b to
bce7123
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source repair follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head bce71233dc79d86ec35990482819a6f59ed5176a, live base 067db608fbc624e56161ccf3e4ff1c0f11c30baf, merge-base 3bcf1c4bb1111d924a84d9b710c1da2db80ccf0b. The prior P2 (4492884106) is addressed in source; no new actionable findings in this focused follow-up. All seven previously reviewed feature patches are retained, including the earlier late-local-row and cached-alias repairs.
elsewhereMatches now recognizes the eligible agent’s exact public key through the shared parser, so hex, npub and nostr:npub queries reach the chooser hold and resolver’s bounded library wait. The resolver also reads offered candidates and local keys when each decision runs. Added regression source covers an empty directory response arriving before library completion, pending resolution for all three key forms, and the hex chooser/Space path through the Bring prompt. Failure, Retry, timeout and candidate eligibility continue to use the existing owners and policies.
Source-only: no tests, builds, app runs, CI verification, visual checks or performance measurements. Author-reported validation was not rerun. The public PR description was inspected and is unchanged; its historical demo video remains unviewed, including its visual/privacy surface.
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 One narrow exact-key selection regression remains.
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head 52794e10267d3fdc59013d017d2eb5cc9f3075c2, target base 067db608fbc624e56161ccf3e4ff1c0f11c30baf, merge-base 3bcf1c4bb1111d924a84d9b710c1da2db80ccf0b. The exact-key namesake gap reported in discussion r4493800251 is addressed in source; no new actionable findings in this focused follow-up. The eight earlier patches and previous repairs are unchanged.
The helper now preserves the specifically identified elsewhere agent despite a local namesake. Both chooser candidate sets depend on the decoded key, and each resolver decision supplies its query. Name filtering, channel admission, local-row precedence for the same key, and the complete-library hold remain intact. The README now explicitly states the key exception. Added regression source covers the completed-library chooser and pending resolution for hex, npub and nostr:npub, ending at the Bring prompt without sending.
Source-only: no tests, builds, app runs, CI verification, visual checks or performance measurements. The author’s reported 1,027 passing tests and fail-before checks were not rerun. The public PR description remains historical and unchanged; its demo video remains unviewed, including its visual/privacy surface.
|
🤖 Must-fix — show loading while the toolbar mention picker waits for agents. With native inventory ready and the local agent library still loading, search the toolbar picker for an agent you own in another community. The new hold hides that agent and matching directory results, but the picker can show “No matching channel members” with no loading feedback after the directory settles. The combined agent status is already Use Evidence: new hold, combined status, picker loading, empty message. This conflicts with the design guide’s loading/empty distinction. Reviewed source at |
52794e1 to
e21a524
Compare
|
🤖 @tellaho Fixed in fb91f81 ( The picker no longer keys its feedback off the combined agent status alone. Side by side, before (left, picker-loading-before-after.mp4
The harness is the real |
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage.
No new actionable findings in this bounded follow-up at e21a5246649a3acee5a8da9c953c01c3cfa3f0b5, against base/merge-base 40e6ccb597cf9d8b47f9692de8285458cc304017.
The picker now distinguishes a pending agent read from an empty result when native inventory is already ready, including invite mode, and suppresses empty copy after an agent-read failure. agentsPending remains separate from selection evidence, preserving usable local choices. We checked the two new patches and their interaction with ranking, punctuation selection, candidate completeness, library reconnect/Retry and the existing exact-key/alias/late-row repairs. The nine previously reviewed feature patches remain unchanged.
Source-only: no tests, builds or app workflows were executed, and CI was not verified. Regression bodies were inspected; the new failure-state test sets the library directly, so clicking toolbar Retry and focus recovery remain unexercised. The PR description, author discussion and sampled frames from both attached demos (eleven historical-demo frames and nine loading-repair frames) were inspected. Both demos show older revisions and do not validate this head. Current native/browser behavior, visual states and performance remain unverified.
e21a524 to
3077f0d
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage.
No new actionable findings in this bounded test follow-up at 3077f0de0a3fdbcfe449bfeb25c08320f1cc22d4, against base/merge-base 58116b826171d190a7376fc57b0509c32c507dde.
The helper now pastes a complete nostr: key after @, avoiding the existing typed-colon terminator, while hex and npub remain typed. Both regression matrices retain their loading, directory-first completion, pending-chip and Bring assertions. We traced the real composer paste path and fixture ordering. All eleven previously reviewed patches and the production mention/input paths remain unchanged; prior source coverage and limitations carry forward.
Source-only: we did not run tests, builds or app workflows, inspect CI logs, or independently verify the commit’s test-pass and React-warning claims. These cases cover pasted nostr: queries, not character-by-character entry. The unchanged public demos show older revisions; browser/native clipboard behavior and prior Retry/focus gaps remain unverified.
3077f0d to
1a59982
Compare
Typing `@Jitter` and Space in a stream or forum left the name as prose when the viewer also had an agent named Jitter in another community. The chooser offered that agent as a local choice, so the name was no longer unique and Space never tagged the member here. Two rules fix this: - The chooser no longer offers an agent from the viewer's other communities when one of the viewer's unarchived agents in this community has the same name. Names are normalized the same way the exact-name check folds case, accents and outer whitespace. `offeredElsewhere` filters `Agents.joinable()` where the chooser and the headless resolver read it. Team eligibility keeps the unfiltered list, so a saved team with such an agent stays usable. The Agents page import list is unchanged. - Agents from other communities never make a name here ambiguous. In `spaceMention`, step 1 considers only this community's local choices. Step 2 takes the unique exact name among choices here first, and an `elsewhere` row only when nothing here has the name. The resolver calls `spaceMention`, so it follows the same rule. Behaviour change: an `elsewhere` row that is the only match no longer selects at once. Like any outside name, it waits for the directory search (as a pending chip) and returns to prose when the search fails, is truncated, or the name is one character long. Its longer names still block Space, as before. README sections 1 and 5 state both rules. The `space` fixtures now run through `spaceMention` step 2 and gain two `elsewhere` cases. Tests: - The MessageComposer test that expected prose now expects the member's tag, both from the chooser and through the resolver. It checks the mention keys (send argument 2). - New: an `elsewhere` row is hidden when a same-named agent of the viewer's is here. - New: an `elsewhere` row that is the only match still selects on Space and opens the "Bring Rocket into" prompt. - With the original production files, the 4 new MessageComposer cases and the new precedence fixture fail. The `elsewhere`-only case fails only on its pending-chip assertion. Validation: - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 931 tests passed. - `tsc --noEmit` passed, and `biome check` on the changed files passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to 73ea38c, addressing its review. It let a non-member directory result beat the viewer's own agent from another community on Space, so Space and the highlighted row could disagree on one name. It also made an other-community agent that was the only match wait for the directory, and that row could appear and then go disabled on a cold start. Space now ranks the choices that have the typed name: 1. A local choice in this community (a member or an offered agent) wins. The viewer's agents from other communities can't be the exact name. 2. Otherwise one of the viewer's agents from another community wins. Directory results can't be the exact name. 3. Otherwise the plain full-set rule applies. `spaceMention` picks the rank and then runs both steps over it. The resolver calls `spaceMention`, so the chooser and the pending-chip path share one rule. `namesQuery` (mention-ranking.ts) is the "has this exact name" check the rank needs. In step 2, a local choice here and a directory result with the same name stay ambiguous, as before. An other-community agent counts as local again (README section 5 items 1 to 3), so when it is the only local match Space tags it at once. Directory results found later rank below it. This reverts the wait-for-search behaviour of 73ea38c, and with it the scale problem: a truncated search (`more`) no longer leaves that name as plain text. Other-community rows are now held until `agentChoices` is no longer pending, so the name rule can't remove a row the chooser has shown: - Ordinary mentions never load the agent library, so `pending` could stay true forever. While it holds rows, the chooser loads the library as invite mode does (`useAgentChoices(session, invite || held)`). - `elsewhereHeld` is true only where such rows would be offered (not in rosters or invite mode, and only where `availableMentionAgents` allows them). DMs and Solo chats are unaffected. - While rows are held, the agent list counts as loading. Space waits as a pending chip, and the resolver waits for the same evidence, so no directory result can win during the wait. - Rows released after the list has installed are added at the bottom (README section 6). README: - Section 1 now points to section 3 for normalization. - Section 5 states the rank and the instant tag. - Section 6 states the hold. - The fixture table says members and `elsewhere` choices are local. Fixtures (step 2, now with members and `elsewhere` choices local): - new: a directory result plus an `elsewhere` agent -> `elsewhere` - new: a member, an `elsewhere` agent and a directory result with one name -> null - kept from 73ea38c: member plus `elsewhere` -> member, and `elsewhere` only -> `elsewhere` MessageComposer tests: - Replaced the test that pinned the wait with one where Space tags the agent at once while the search is still running. - New: the viewer's agent outranks a directory result of the same name, from the chooser and through the resolver. - New: the hold. The row never appears when one of the viewer's agents here has its name, and it is added last otherwise. - New: a pending chip settles once the library loads, without the directory read. - The archived-elsewhere test now calls `h.setLibrary([rocket])`, which covers the `!archivedMention` filter. - Against 73ea38c's production files, all 6 new cases and the new directory-result fixture fail. With only the hold removed, the hold cases fail. With the archived filter removed, the archived test fails. Single-community behaviour is unchanged. With no other-community agents, `elsewhereHeld` is false, `offeredElsewhere` returns early, every rank reduces to the old `exactMention` calls, and the resolver waits only in invite mode, as before. Validation: - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 938 tests passed. - `tsc --noEmit` passed. `biome check` on the changed files and `git diff --check` passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app, including how long the hold lasts on a cold start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to c39512a, addressing its review. 1. Agents released late never land below directory rows. On a cold start, `@goose` in a stream could install a stranger `Goose` from the directory as row 0 while the library loaded. The viewer's `Goose · In b.example` then appeared at the bottom. Space picked the viewer's agent, but Enter and Tab picked the highlighted stranger. While rows are held for the query, directory rows now stay out of `outside`. After release, each batch adds other-community agents first, then directory people (`[...rank(elsewhere), ...rank(directory)]`), because `rankMentions` puts other-community agents after directory people in one batch. 2. The hold now delays only the names it can affect. `elsewhereHeld` is now `elsewhereMatches`, with no pending check and an optional query. It is true when the chooser would offer an agent from another community whose name `matchPerson` matches the query. - The chooser's `held` is `agents.pending && elsewhereMatches(..., query)`. That drives `pending` and keeps directory rows waiting. - The resolver waits for the library only when `elsewhereMatches(..., name)` holds. Longer names start with `name`, so they are covered too. - During the cold window, `@alice ` tags a fully named member at once again. The chooser shows "Searching community…", not "Loading recipients…". 3. Retry rereads the agent library. The chooser now returns `includeLegacy`: open and either invite mode or `elsewhereMatches(...)` with no query. It replaces `!!invite || held` as the `useAgentChoices` demand. Retry in MentionCompletion and "Retry agent list" in MentionPicker pass `model.includeLegacy || teams.includeLegacy` to `agentChoices.refresh`, so a failed library read can recover. Behaviour change: in a channel that offers such agents, the open chooser now keeps the library retained, not only while it loads, as invite mode already does. 4. README section 6 now says these agents show only while the viewer's agents here are known, so a shown row is not disabled once they are known. A reload (Retry, `refresh({ fresh })`, a disconnect) still disables shown rows in place until it lands, as it does for offered agents here. A query that matches one of their names waits, and directory people wait with it, so section 5's "Local choices show before directory results" holds. Differences from the review's proposal: - "Keep directory rows out of `outside` while held" is gated on the narrowed, per-query hold, not on every query while the library loads. A query that matches no held agent has no late local row to order, so its directory rows don't wait. - The gate matches the name `joinable()` gives, as proposed. A row that matches only through another name known here, such as a profile name cached for an agent archived here, is not held for that query. That is rare, and I did not cover it. Tests (MessageComposer.test.tsx; `setLibrary` takes an optional error): - New, for Space and for Enter: the search finds a stranger `Rocket` before `setLibrary`. Rows are then viewer's agent first, stranger second, and both keys lead to "Bring Rocket into". - New, from the chooser and through the resolver: while the library loads, a member named Alice is tagged, and the chooser shows "Searching community…". - New: after a failed library read, "Retry suggestions" calls `agentLibrary.refresh`, and the error clears when the library loads. Fail-before: - With c39512a's production files, all 5 new cases fail. - With only fix 1 reverted, both ordering cases fail. Without their order assertions, Enter still fails because it picks the stranger while Space picks the viewer's agent. - With only the chooser's narrowing reverted, both Alice cases fail. With only the resolver's narrowing reverted, the resolver case fails. - With only the Retry change reverted, the Retry case fails (0 calls). Single-community behaviour is unchanged. With no agents in other communities, `elsewhereMatches` is false. Then `includeLegacy` is `open && !!invite`, `held` is false, `outside` is the old directory rank, and Retry passes the same value as before. Validation: - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 943 tests passed. The new cases passed in 3 more runs. - `tsc --noEmit` passed. `biome check` on src/bundled/mentions and the test file, and `git diff --check`, passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead), the picker's "Retry agent list" (it shares `model.includeLegacy` with the tested inline Retry), and a check in the app, including how long the cold-start hold lasts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to 2692f6e, addressing its review. 1. No cold-start wait on the first `@`. Before, an offering channel loaded the legacy agent library only once the chooser opened. The first matching `@` of a session then waited for that read, and so did the resolver. The resolver shares the 8 s `MENTION_RESOLVE_TIMEOUT_MS`, so a slow read could send Space back to prose. Now `useMentionChoices` demands the library whenever `elsewhereMatches(...)` holds, open or closed. The composer's mention picker stays mounted while closed, so its chooser retains and ensures the library through `useAgentChoices` when the composer mounts. The library is then normally `ready` before the first `@`. - Released by the same effect on unmount, or when the channel no longer offers such agents (a channel switch remounts the composer). - Not started for DMs, Solo chats, rosters, invite mode, or an empty `joinable()`: `elsewhereMatches` is false there. - Invite mode still demands the library only while the chooser is open, as before. README section 6 says a client SHOULD start this read when such a composer opens. 2. Retry rereads the agent library only when it failed. Inline Retry passes `(model.includeLegacy && (agents.status === "error" || !!agents.error)) || teams.includeLegacy`. A Retry for a directory, archive, membership or name error no longer reloads a ready library. That reload disabled shown other-community rows ("No longer available…") until it landed. Behaviour change: this also applies to invite mode, which before reread a ready library on every Retry. The picker's "Retry agent list" is unchanged. It renders only when `agents.status === "error" || agents.error`, so it already follows this rule. Tests (MessageComposer.test.tsx): - New: with the library idle, mounting the real MentionPicker in a stream composer starts the library read before any `@`. In a DM it does not. The stream case asserts `toHaveBeenCalled`: the fixture mounts under StrictMode and its `refresh` stub never goes to `loading`, while the real library joins the read in flight. - New: a Retry for a failed directory search in an offering channel does not call `agentLibrary.refresh`, and the other-community row stays enabled. - Kept: Retry after a failed library read still rereads it. Fail-before: - With only the chooser demand reverted, the stream mount case fails. - With only the Retry gate reverted, the no-reread case fails. Validation: - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 946 tests passed. - `tsc --noEmit` passed. `biome check` on src/bundled/mentions and the test file, and `git diff --check`, passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead), a test that the demand is released on unmount (the fixture stubs `retain`), and a check in the app with a real account. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…ts namesake elsewhere Checks a case on top of 2fadf38: the viewer owns an agent "Goose" in this community that is not a member of the current stream, and another "Goose" in another community. This already works, so the commit adds no production change. It pins the behaviour with tests. Why it already holds: - `offeredElsewhere` (mention-candidates.ts) compares names with `session.agentChoices.snapshot().identities`, the viewer's unarchived agents in this community, not channel members. So the "In <host>" row for the other Goose is never offered, as README section 1 item 3 says. - In a stream or forum where the viewer can add members (outbox supports 9000), `availableMentionAgents` offers the viewer's agents that are not members. Goose here gets a "Not in channel" row, and `mentionAdmission` admits it because streams allow outside mentions. - That row comes from `mentionCandidates` without the directory, so it is in `local` in the chooser (`localKeys`) and in the resolver. `spaceMention` therefore treats it as a local choice in this community (section 5, item 2): step 1 selects it at once, and step 2 ranks it above an agent from another community. It is not handled as a directory stranger. Tests: - MessageComposer: "tags the owner's agent here that is not in the channel, not its namesake from another community", from the chooser and through the resolver. Only the "Not in channel" Goose row shows. Space tags it. Submit asks "Goose is not in this channel." (not "Bring Goose into"), and "Send anyway" sends its key as the outside recipient. - New `space` fixture `agent_offered_here_outside_the_channel_outranks_its_namesake_from_another_community`: an offered non-member agent and an `elsewhere` agent share a name, and the offered one wins. The fixtures had no way to mark an offered (section 1, item 2) non-member as local, so choices gain an optional `offered` flag (default false). The runner counts members, `offered` choices and `elsewhere` choices as local. The README fixture table and the fixture `$comment` say so. The fixture version stays 2: the field is optional, and this repository's test is its only reader. Fail-before (production files mutated, then restored): - `offeredElsewhere` counting only the viewer's agents that are channel members: both MessageComposer cases fail (two Goose rows). - `spaceMention` treating offered non-members as non-local: the new fixture fails (it picks the agent from another community), and so does the chooser case (Space leaves a pending chip). The resolver case still passes there, because rule 1 already hides the other row. - The runner without `offered`: the new fixture fails. Single-community behaviour is unchanged: no production file changed. When the relay cannot add members, neither Goose is offered, as before. Validation: - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 947 of 949 passed, at load average about 70 to 120 on 16 cores. The 2 failures were in tests this commit does not touch: one MessageComposer test ("asks a mentioned agent of the viewer's for a model and effort on the next run only") hit the 5 s timeout, and one Solo chat test in SoloDraft ("tags an agent whose name was typed with Space while a retry after a failed inventory read loads") lost its chip. Both passed when rerun alone, and SoloDraft.test.tsx then passed as a whole (89 tests). - `tsc --noEmit` passed. `biome check` on src/bundled/mentions and the test file, and `git diff --check`, passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to 5c9dde7, addressing the GitHub review on PR 2581 (discussion r4481855494). The name rule (README section 1, item 3) hid an agent from another community only when one of the viewer's agents here had its name, and it trusted `agentChoices` as soon as it was no longer pending. A failed library read is not pending, but it leaves out the library's agents. So when the viewer's Jitter here was only in the library and native inventory listed a Jitter elsewhere, a cold read failure showed the "In <host>" row. Space then tagged that key, submit asked to bring it in, and a Retry that found the local Jitter disabled the shown row. `agentChoices.complete` is the evidence docs/agents.md requires for inference: a ready library with no errors, and a local source that is ready or unavailable. All three places now require it: - `offeredElsewhere` returns none until `complete`, not until `!pending`. - The chooser's `held` is `!agents.complete && elsewhereMatches(..., query)`. Held rows and directory people stay out of `outside` until a complete read. Only a read in progress counts as loading (`pending` includes `held && agents.pending`). After a failure, the chooser shows the local rows it has, the agent error, and Retry. `SpaceEvidence.complete` now also requires `!held`, so Space takes only step 1 (a unique local name) and otherwise stays prose. - The resolver, after its existing wait, returns null when the name matches such an agent and the read is still incomplete. It does this after the local step and before the directory read, as the chooser does. Retry is unchanged. It rereads the library when it failed, because `agents.status === "error" || agents.error` still holds. Once the read lands, the rule works as it does after a successful first read. A partial read (a ready library with `error`) and a failed native source are also not complete, so they hold such rows the same way until Retry. README section 6 now says these agents show only once a complete read has listed the viewer's agents here. A failed or partial read is not evidence. While the read runs, the agent list is loading. After it fails, only the local step applies to a matching query, and a retry reads again. Single-community behaviour is unchanged. With no agents in other communities, `elsewhereMatches` is false, so `held` is false and the resolver gate never applies. DMs, Solo chats, rosters and invite mode are unaffected for the same reason. Tests (MessageComposer.test.tsx), each from the chooser and through the resolver. The resolver cases hold its member-name read with a gate that is released in `onTestFinished`, so the chip can be observed: - New: the library read fails. The viewer's Jitter here is only in the library, and their Jitter elsewhere is in native inventory. The "In oss.example.com" row never shows, and Space leaves "@jitter " as prose with no recipient. In a second mention, Retry calls `agentLibrary.refresh`, and the library returns the local Jitter. The only row is then "Jitter · Not in channel", and Space tags it. Submit asks "Jitter is not in this channel.", and "Send anyway" sends the local key. - New: during the same failure, a directory result named Jitter is not shown, and Space does not tag it. Fail-before (production files mutated, then restored): - With 5c9dde7's production files, all 4 new cases fail. - With only `offeredElsewhere` reverted, 3 fail. The resolver directory case still passes because of the resolver gate. - With only the chooser hold reverted, the chooser directory case fails. - With only the resolver gate reverted, the resolver directory case fails. Validation: - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 953 tests passed, at load average about 9 to 16. The new cases passed in 3 more runs. - `tsc --noEmit` passed. `biome check` on src/bundled/mentions and the test file, and `git diff --check`, passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead), the reviewer's modeled HTTP 502 in Chromium and WebKit, and a check in the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to 3ee7539, addressing the GitHub review on PR 2581 (discussions r4489800862 and r4489800870). 1. Offered agents here that load late are added (P2). In a stream where the viewer can add members, take a member `Jitterbug`, the viewer's agent `Jitter` in another community, and the viewer's non-member agent `Jitter` here that only the loading library lists. `@jitter` installed the member's row at once. When the library loaded, the name rule hid the other community's Jitter and the Jitter here became a local choice. But a non-invite chooser appended only `outside()` (other-community agents, then directory people), so the Jitter here never showed. `spaceMention` still chose its key. Because it was not a shown item, completion.ts dropped it, and no pending resolver was published, so Space left the name as prose. The append step now adds offered non-member agents here (local, not members, not from another community) before `outside()`. Shown rows stay where they are, and member-list changes still keep their installed set. This also covers an offered agent that arrives late in a single community, which before was never added for an unchanged query. Invite mode is unchanged. 2. The hold matches the names cached here (P3). `elsewhereMatches` matched only the name from `joinable()`. The agent's eventual row also matches the names it has without the directory (`buildCandidates` aliases): its cached profile name here, the verified full name and username in that profile, and its names among the viewer's agents here. So for an agent archived here as `Comet` that is `Rocket` in another community, `@comet` did not hold. A directory stranger named Comet showed and took Space while the library loaded. Once the library landed, the agent was added below the stranger: Space chose the agent, and Enter chose the stranger. The resolver did not wait either, and tagged the stranger. `elsewhereMatches` now checks all of those names. Undefined names are skipped; the `joinable()` name is checked as before. README section 6 states both rules: an agent from another community is held by the names its row has without the directory search, including aliases cached here; and late offered agents are added before other-community agents and the directory people added with them. Tests: - MessageComposer, from the chooser and through the resolver: member `Jitterbug`, library loading, a namesake `Jitter` elsewhere. The search finds `Jitterbird` during the hold. Once the library lists the Jitter here, rows are Jitterbug, then Jitter (Not in channel), then Jitterbird. Space tags Jitter. Submit asks "Jitter is not in this channel.", and "Send anyway" sends its key. In the resolver variant, Space during the load leaves a pending chip that settles to the same key. - MessageComposer (`mountCachedAlias`), for Space and for Enter: the agent archived here as Comet, library loading, and the search finds a stranger Comet. While the library loads, the chooser shows "Loading recipients…" and no Comet or Rocket row. Once it loads, rows are the agent (In oss.example.com) above the stranger (Not in channel). Both keys lead to "Bring Rocket into". - MessageComposer: a pending `@comet` chip typed during the load settles to the agent, not the stranger. - mention-candidates.test.ts: `elsewhereMatches` is true for every alias of the agent's elsewhere row (joinable name, cached profile name, verified full name and username, name here) and false for an unrelated name. Fail-before (production files mutated, then restored): - With only the append change reverted, the P2 chooser case fails (2 rows, not 3). The P2 resolver variant still passes: the resolver never read shown rows, so it pins parity only. - With offered agents appended after `outside()`, the P2 chooser case fails on row order. - With only the `elsewhereMatches` change reverted, all 3 P3 MessageComposer cases and the unit test fail. The chooser cases fail at "Loading recipients…"; the resolver case does not settle to `@Rocket `. - Without just the names here, the verified names, or the cached profile name, the unit test fails. Without the cached profile name, the 3 P3 MessageComposer cases fail as well. Validation (load average about 5 to 6 on 16 cores): - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 1000 tests passed. After a last change to the alias guard (skip only undefined names), mention-candidates.test.ts and the 29 MessageComposer cases for agents from other communities passed again. - `tsc --noEmit` passed. `biome check` on the changed files and `git diff --check` passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to bdb4d9b, addressing the GitHub review on PR 2581 (discussion r4492884106). `elsewhereMatches` matched one of the owner's agents from other communities only by its names. Its exact public key (hex, npub or `nostr:npub`) is the exact name of the choice with that key (`matchingMentions`, `exactMention`), but no name matches it. So while the owner's agents here loaded, `@<key>` of such an agent did not hold: `offeredElsewhere` left the agent out until a complete read, the chooser searched the directory, and when the author lookup found no profile here, the chooser finished with no recipient. A pending chip skipped the library wait in the resolver as well, captured an empty `offered` set, and returned the key to prose after the empty lookup. - `elsewhereMatches` now treats a query that is a public key (`typedPublicKey`) as matching only the agent with that key, before the name aliases. The chooser's `held` and `includeLegacy`, and the resolver's wait and incomplete-read gate, all read it, so a key takes the same bounded library wait a name does. - The resolver reads `offeredElsewhere` and the local key set when it decides, not once before the directory lookup, as the chooser does on each render. A library read that lands during the lookup offers its agents to that decision. With the key hold in place the resolver always waits first, so no test pins this move on its own. README section 6 says an agent's exact public key holds it too. Tests: - mention-candidates.test.ts: `elsewhereMatches` is true for the agent's hex key, npub and `nostr:npub`, and false for a member agent's key and npub. - MessageComposer, for hex, npub and `nostr:npub`: the library is loading, the typed key shows "Loading recipients…" with no row, the directory finds no one, Space leaves a pending chip, and once the library lists the owner's agents here the chip settles to `@Rocket `. Submit asks "Bring Rocket into". - MessageComposer, hex key, from the chooser: after the empty lookup and the library read, the only row is the agent's "In oss.example.com" row, and Space tags it. Fail-before (production files replaced with bdb4d9b's, then restored): all 5 cases fail. The composer cases never see "Loading recipients…". With only the resolver change reverted, the 4 composer cases still pass. Single-community behaviour is unchanged: with no agents in other communities `elsewhereMatches` returns false before the key check, and the resolver's decision reads the same empty `offered` set as before. Validation (load average about 22 to 34 on 16 cores): - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 10 files, 932 tests passed. - `tsc --noEmit` passed. `biome check` on the changed files and `git diff --check` passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to bce7123, addressing the GitHub review on PR 2581 (discussion r4493800251). Say the owner has Jitter here (key A) and Jitter in another community (key B). The name rule (README section 1, item 3) dropped B in `offeredElsewhere` before the query was considered. So typing B's hex key, npub or `nostr:npub` could never select B, although a key names only its owner. The exact-key hold from bce7123 also waited for the library and then offered nothing: the chooser finished with no row, and a pending chip returned to prose after the directory lookup. `offeredElsewhere` now takes the query. It applies the same-name exclusion only to name queries and keeps the agent whose key `typedPublicKey(query)` returns. - The chooser passes the typed key to both `current` and `localKeys`, and depends on that key rather than the query. Other keystrokes keep the cached choice set. - The resolver passes the query it decides, so continued names stay name queries. The key still waits for a complete read of the owner's agents here, like a name. `joinable()` leaves out only agents that native inventory lists here, and the library can still list that key here. Until a complete read, it is unknown whether the key has a row here or an "In <host>" row. So the hold, Retry and the incomplete-read gate are unchanged. In `spaceMention`, a key query matches only B (`matchingMentions`), and no name equals a key, so A's name cannot make it ambiguous. A new `space` fixture pins this: a member Jitter and a Jitter from another community, with B's npub as the query, select B. README section 1 item 3 states the key exception. Section 6 says that the key still holds the agent until the complete read, and why. Tests: - mention-candidates.test.ts: with a complete read, a namesake elsewhere is not offered for no query, "", or its name in either case. It is offered for its hex key, its upper-case hex key, its npub and its `nostr:npub`, beside the other agent. The other agent's key does not offer it. After an incomplete read, its key offers nothing. - MessageComposer, from the chooser: the library lists Jitter here. The only row for B's hex key is "In oss.example.com". Space tags "@jitter " at once, and submit asks "Bring Jitter into". - MessageComposer, through the resolver, for hex, npub and `nostr:npub`: the library is loading and the directory finds no one. Space leaves a pending chip. The library then lists Jitter here, and the chip settles to "@jitter ". Submit asks "Bring Jitter into". Fail-before (production files replaced with bce7123's, then restored): - All 3 production files reverted: the unit test and all 4 composer cases fail. - Only use-mention-choices.ts reverted: only the chooser case fails. - Only resolve-space-mention.ts reverted: only the 3 resolver cases fail. - The new fixture passes before and after. It pins `spaceMention`, which did not change. Single-community behaviour is unchanged. With no agents in other communities, `offeredElsewhere` returns before reading the query. For a name query, `typedPublicKey` returns undefined and the filter is the same as before. Validation (load average about 35 to 44 on 16 cores): - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 1027 tests passed. - `tsc --noEmit` passed. `biome check` on the changed source, test and fixture files, and `git diff --check`, passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to 52794e1, addressing the GitHub review on PR 2581 (issue comment 6702822296). With native inventory ready and the agent library still loading, a search in the toolbar mention picker for an agent the owner has in another community held that agent and the directory people with it (README section 6). But the picker showed "Loading agents…" only while the combined agent status was `loading`, and `createAgentChoices` reports `ready` as soon as native inventory is, with the library read still `pending`. So the hold showed nothing, and once the directory search settled with no one, "No matching channel members." showed although the row was still to come. The design guide's "States, feedback, and motion" section keeps loading distinct from no results. The picker now has one `loading` flag: the chooser is pending (`model.pending`, which includes members, a draft roster, invite mode, and the hold while the library read runs) or the agent list is `loading`. It drives the existing "Loading agents…" text and suppresses "No matching channel members." while it holds. The second term keeps the existing feedback for a plain channel whose agent list loads while the chooser is not pending, which `model.pending` leaves out by design so Space still works there. The inline chooser has no such gap: MentionCompletion already puts "Loading recipients…" first in its status chain when `model.pending`, and it has no empty message. A new test pins that. README section 6 now says a chooser SHOULD show that it is loading, not that nothing matches, while this read runs, even when its agent list is otherwise ready. Tests (MessageComposer.test.tsx): - `mountDirectoryMention` takes an optional `control`, the native agent inventory, so a test can hold the library with the combined status `ready`. `mountNativeReadyHold` asserts that status. - New, toolbar picker: searching `rocket` shows "Loading agents…" and no "No matching channel members.", before and after the directory settles with no one. Once the library loads, the agent's "In oss.example.com" row shows and both messages are gone. - New, inline chooser: the same hold shows "Loading recipients…" before and after the directory settles, then the row. - The picker tool contribution used by the mount test moved into a shared `pickerOnly` fixture, which the new toolbar test reuses. Fail-before (production file replaced with 52794e1's, then restored): the toolbar case fails at its first assertion, with no "Loading agents…". With only the `!loading` gate removed from the empty message, it fails at "No matching channel members." being shown. The inline case passes before and after: it pins parity. Single-community behaviour is unchanged: with no agents in other communities and no invite, `model.pending` is true only while members or a draft roster load, when the picker had no rows to show anyway. Validation (load average about 380 to 570 on 16 cores): - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 1027 of 1029 passed. The 2 failures hit the 5 s timeout in tests this commit does not touch ("sends and restores model and effort per Solo chat and agent" and "a refused start reports why and never overwrites settings begun in New chat since"); both passed when rerun alone. - `tsc --noEmit` passed. `biome check` on the changed files and `git diff --check` passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app, including how the picker looks while held in both themes. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…nts wait Follow-up to dcae5f69f, addressing its review. That commit gave the toolbar mention picker one `loading` flag, `model.pending || agents.status === "loading"`, and gated "Loading agents…" and "No matching channel members." on it. The review found three states it still got wrong: 1. Invite mode had the same loading-vs-empty gap. With native inventory ready and the library pending, the chooser's invite term in `pending` is false by design (Space still tags a unique local name), so `loading` was false. But the install gate (`!invite || !agents.pending`) withholds rows until the library lands, so a query that matched no local row showed "No matching channel members." and then the row appeared. 2. After a failed library read, `agents.pending` is false and the install gate withholds rows on `agents.error`, so the empty message rendered beside "Retry agent list". 3. `loading` also covered `membersPending` and a draft roster, so "Loading agents…" rendered beside "Channel membership unavailable." while membership loaded, with no agent read involved. Rule: "Loading agents…" names the agent read only; the empty message waits for every pending state and for a failed agent read. - `useMentionChoices` returns `agentsPending`: `agents.pending && (!!invite || held)`, the read of the agent list that rows wait on. It mirrors the install gate's agent terms and is not folded into `pending`, so Space in invite mode is unchanged. Its `pending` is unchanged. - The picker's `agentsLoading` is `agents.status === "loading" || model.agentsPending`. It drives "Loading agents…". The string is unchanged. - `agentsFailed` (`agents.status === "error" || !!agents.error`) names the existing Retry condition. - `empty` requires members, `!model.pending`, `!agentsLoading`, `!agentsFailed`, no candidates and no teams. It drives "No matching channel members.". README section 6 now says the same loading-over-empty rule holds while inviting agents waits for the agent list, and that after the read fails a chooser SHOULD NOT say that nothing matches. Single-community behaviour otherwise unchanged: with no invite and no agents in other communities, `agentsPending` is false, so "Loading agents…" shows exactly when the agent status is `loading`, as before dcae5f69f, and the empty message is additionally suppressed only while membership or a roster loads (no rows then anyway) or the agent read has failed. Tests (MessageComposer.test.tsx; `mountDirectoryMention` takes an optional `inviteAgents`, and `openPicker` opens the toolbar picker): - New, invite mode with native inventory ready and the library loading: searching `rocket` shows "Loading agents…" and no empty message; once the library lists Rocket, its row shows and both messages are gone. - New, failed library read: searching `rocket` shows "Retry agent list" with neither message; once the read lands with no Rocket, the empty message shows and Retry is gone. - New, membership loading: the picker shows "Channel membership unavailable." and neither "Loading agents…" nor the empty message. Fail-before (production file replaced, then restored): - With dcae5f69f's MentionPicker.tsx, all 3 new cases fail. - With `model.agentsPending` dropped from `agentsLoading`, the invite case fails, and so does the existing other-community hold case. - With `!agentsFailed` dropped from `empty`, the failed-read case fails. - With "Loading agents…" gated on `model.pending || agentsLoading`, the membership case fails. Validation (load average about 190 to 250 on 16 cores): - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 1032 tests passed. - `tsc --noEmit` passed. `biome check` on the changed files and `git diff --check` passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app, including the invite-mode picker and the failed-read picker in both themes. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
… colon ends a name Follow-up to e21a524, fixing the CI failure on PR 2581 (run 42589272929, JavaScript 1/6 and both Flake check jobs). The two `nostr:npub` cases of "settles a pending mention of the %s of the owner's agent from another community into it once their agents here load" failed at `findByText("Loading recipients…")` in every run. They typed `@nostr:npub1…` key by key. Since #2713 (cae3922, in this branch's base after the last rebase), `:` is a mention terminator: typed straight after a query it selects the exact name, or holds the query as a pending mention while the chooser is undecided (mentions README section 5, `mentionTerminator`). With the directory still searching `nostr`, the colon committed `@nostr` as a pending chip, and `:npub1…` was typed as prose after it (the CI DOM shows a chip with `data-source="@nostr"` and the key as trailing text). No key query ever reached the chooser, so nothing was held. The hex and npub cases have no colon and passed. This is the documented product rule, not a defect in the exact-key hold (bce7123, 52794e1): a `nostr:` key reaches the chooser pasted, as a 63-character key does in practice, and the hold then applies to the whole query. The tests now enter the key as a user would: `enterKey` types `@` and the hex key or npub, and types `@` then pastes a `nostr:` key. The assertions are unchanged. The "Maximum update depth exceeded" warning that CI printed for these two cases is not a render loop. Recording commits with a React DevTools hook showed one commit per keystroke; the warning comes from React's nested-passive-update counter, which `setPasteTooLong(false)` in the composer's draft effect (92d4c36, on main) increments on every keystroke whose passive flush runs while the next keystroke's update is already pending, so that 60-odd keys typed after a pending chip cross its limit of 50. The paste removes the long typed run, and the warning no longer appears. The effect itself is main's code and is left alone. Fail-before: with the colon typed, both cases fail as in CI; with the paste, all 6 variants pass. Validation (load average about 5 to 9 on 16 cores): - `vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.ts`: 11 files, 1063 tests passed, with no "Maximum update depth" line on stderr. - The two `nostr:npub` cases passed in 3 more runs each, with no warning on stderr. - `tsc --noEmit` passed. `biome check` on the test file and `git diff --check` passed. - Not run: the browser journeys (mention-rules, completion-work, typeahead) and a check in the app. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
1a59982 to
1d9cfce
Compare
Problem
Since #1601, the mention chooser in streams and forums also offers the viewer's own agents from their other communities ("Jitter · In feedback…"). The Space rule from #935 and #1623 tags a typed name only when exactly one choice has that exact name, and it counted those other-community agents as local choices.
So a viewer with two agents named Jitter, one in this community and one in another, got two exact matches for
@Jitter. Space stayed a plain space, the message went out untagged, and Jitter never woke. The same happened to a member who shared a name with one of the viewer's agents elsewhere (a personMiravs. the viewer's agentMirain another community). An existing MessageComposer test pinned this as expected behaviour. Before #1601,@Jitter+ Space tagged the Jitter here, which is why the report says "it used to work".Change
All of this lives in the mentions plugin (
src/bundled/mentions). The chooser and the headless pending-chip resolver sharespaceMention, so they follow the same rules.Rule 1: hide same-named other-community agents. The chooser no longer offers an agent from another community when one of the viewer's unarchived agents in this community has the same normalized name, whether or not that agent is in the channel (
offeredElsewhereinmention-candidates.ts). Team eligibility and the Agents page import list still use the unfiltered list. An agent archived here can still be brought back by mention.Rule 2: Space precedence (here → own elsewhere → directory). Among choices with the typed name:
In step 2, a local choice here and a directory result with the same name are still namesakes, as before.
The instant tag stays for elsewhere-only names. When the only local match is the viewer's agent from another community (for example
@Rocket), Space tags it at once, as onmain. A directory result found later can't make it ambiguous, and a truncated directory search can't turn it back into prose.The cold-start hold covers only matching names. Rule 1 needs to know the viewer's agents here. Until they are known, other-community rows are held, but only for queries that match one of their names (
elsewhereMatches). Directory rows for that query wait too, so a late-released agent never lands below a directory stranger, and Space and Enter can't pick different rows. Other names, such as@alice, tag a fully named member at once.Library prefetch when the composer mounts. In channels that offer such agents, the composer's mounted mention picker now loads the viewer's agent library before the first
@. This avoids a cold read that shares the resolver's 8 s timeout and could send Space back to prose. It does not start for DMs, Solo chats, rosters, invite mode, or when there are no other-community agents.Retry rereads the library only when it failed. Retry for a directory, membership or name error no longer reloads a ready library, which used to disable shown rows until the reload landed. This also applies to invite mode.
Behaviour in a single community, with no agents elsewhere, is unchanged.
elsewhereMatchesis false there, so every rank reduces to the oldexactMentioncalls.Spec (
src/bundled/mentions/README.md)spacefixtures now run §5 step 2 with the precedence. Members and choices markedofferedorelsewherecount as local. There are new cases for each precedence tier and one for an offered non-member vs. its namesake elsewhere.offeredis a new optional field (default false), and the fixture version stays 2.docs/buzz-help.mddoes not describe the Space-tag rule, so it isn't updated.Validation
Unit and component suites
2fadf38e4:vitest run src/bundled/mentions MessageComposer.test.tsx SoloDraft.test.tsx person-match.test.ts relay/mentions.test.tspassed all 946 tests in 11 files.5c9dde762(adds tests only), on a heavily loaded machine (load average about 70–120 on 16 cores): 947 of 949 passed. Both failures were in tests this branch doesn't touch. "asks a mentioned agent of the viewer's for a model and effort on the next run only" hit the 5 s timeout, and a SoloDraft retry test lost its chip. Both passed when rerun alone, andSoloDraft.test.tsxthen passed as a whole (89 tests).tsc --noEmit,biome checkon the changed files, andgit diff --checkpassed.5c9dde762has no production change). Each commit message lists which change fails which case.AI review rounds
73ea38cc8→c39512ac6. The review found that a non-member directory result could beat the viewer's own other-community agent on Space, so Space and the highlighted row disagreed. An elsewhere-only name also waited for the directory and fell back to prose on a truncated search, and a row could appear and then go disabled on a cold start. Changed: the three-tier precedence, the instant tag restored, other-community rows held until the viewer's agents here are known, and a test for the archived-agent filter.c39512ac6→2692f6e54. A late-released elsewhere row could land below a directory stranger, so Enter and Space picked different rows. The hold also delayed every query,@aliceincluded, and Retry never reread a failed library. Changed: directory rows wait while held and each batch puts elsewhere rows first, the hold covers only matching names, Retry rereads the library, and the §6 wording was tightened.2692f6e54→2fadf38e4. The ship gate: the first matching@of a session waited for a cold library read that shares the 8 s resolver timeout. Retry also reread a ready library. Changed: the library loads when an offering composer mounts, and Retry rereads only on failure.2fadf38e4. "I'd ship this: prefetch lifecycle and Retry gate are sound, in-app check pending." It verified the prefetch effect's retain/release lifecycle, that it is gated to offering channels, the StrictMode double call, and the invite-mode Retry change. No blocking findings; the in-app check is still pending.5c9dde762is a test-only commit, not a review round. It pins a case the author raised: the viewer owns an agent here that isn't in the channel, plus a namesake elsewhere. That already works, through rule 1 and the "Not in channel" row counting as local. MessageComposer tests and aspacefixture now cover it.Not run, and still needed
mention-rules,completion-work,typeahead) were not run.retain), and the picker's "Retry agent list" (it shares its gate with the tested inline Retry).--no-verify, so the pre-push hooks did not run on the push. It is based onebbdb5a69and has not been rebased onto currentmain.Jitterhere and anotherJitterin another community: type@Jitter, and only one Jitter row should show (no "In …" row). Press Space and it should become a chip; send, and Jitter should wake. Control:@plus a name that only your agent elsewhere has should chip at once, and sending should ask "Bring … into …?".Demo
Left: merge-base
ebbdb5a69. Right: branch head5c9dde762. Same typing in a #launch stream; captions name who owns each row.elsewhere-before-after.mp4
@Jitterplease look, Enter@Mira, Space,can you review, Enter@Rocket, Space,ship it, Enter@Goose, Space,hi, Enter, then "Do nothing"@Goose, Space,hi, Enter@alice, Spaceand @Jitter, Space,look, EnterSegments 5b and 6 can't show a difference. The bugs that
c39512ac6and2692f6e54fix were introduced within this branch, and in this fixture both builds read your agents here when the composer mounts. After an agent is tagged, the right pane refills@Jitter. That is the existing follow-up-draft behaviour.Not in this PR
aria-label="Person Rocket"(or "Person Goose"), while agents here read "Agent …". This happens onmaintoo (segments 4 and 5b) and is left unchanged.🤖 Generated with Claude Code