Skip to content

fix(mobile): tell same-name agents apart in Solo - #9321

Open
loganj wants to merge 3 commits into
mainfrom
loganj/mobile-solo-agent-names
Open

loganj wants to merge 3 commits into
mainfrom
loganj/mobile-solo-agent-names

Conversation

@loganj

@loganj loganj commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

In Solo, two agents with the same name looked identical: the agent picker, the new-chat draft ("Message Atlas"), the chat header and its agent switcher, and the "working" label all showed the raw name. Now they use the same disambiguated labels as the rest of the app, so same-name agents read differently everywhere in Solo, and one agent reads the same in every place.

Related issue

None found.

Testing

Widget tests open the picker and header with two same-name agents, switch between them, and archive one (the remaining agent goes back to its plain name). Further tests cover a saved agent switch and a resumed new chat that still point to an archived agent with a live namesake, and Solo rows with no working agent building no names. A focused test covers which name Solo shows. Each fails without its change.

Android emulator, local relay, fictional names. Two live agents are both named Helper; Atlas and Scout each have an archived namesake, which Solo already left out.

New Solo chat › Choose an agent: two agents named Helper

Before After

New-chat draft with one Helper picked

Before After

Chat settings › Agent row of an existing Solo chat

Before After

Agent switcher opened from that row

Before After

Details

  • Names are compared only across the agents Solo offers. Archived agents are already left out of that list, so an archived namesake adds no qualifier.
  • A saved choice can still point to an archived agent: a pending agent switch (its notice and the Resume/Discard dialog) or a new chat waiting to be resumed. That agent is labeled against the live agents, so it does not read exactly like a live namesake.
  • A Solo row reads names only while an agent is working in it, and working rows share one name context.
  • Solo keeps showing the owner's own name for an agent (from their agent list) ahead of the agent's self-published profile name. A verified name still wins over both.
  • An agent with no name now shows the short npub, like every other picker, instead of a truncated hex key.

🤖 Generated with Claude Code

loganj and others added 2 commits November 10, 2026 10:33
The Solo agent picker, the new-chat draft, the chat header and its
agent switcher, and the "working" label showed each agent's raw name,
so two agents with one name looked the same. They now use the shared
identity-names labels, scoped over the agents Solo offers. Archived
agents are already left out of that list, so an archived namesake adds
no qualifier.

Solo keeps showing the owner's inventory name ahead of the agent's own
profile name. An agent with no name now shows the short npub every
other picker shows.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Solo shows the owner's inventory name ahead of an agent's cached profile
name, and a verified name ahead of both. The widget tests used one name
for both sources, so nothing caught losing that preference.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj
loganj marked this pull request as ready for review November 10, 2026 16:14
@loganj
loganj requested a review from a team as a code owner November 10, 2026 16:14
@loganj
loganj deployed to codex-review November 10, 2026 16:15 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 10, 2026
@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: b40f22114bac744d91c8afb25d644826646fb776...6bd089443d02378b441be2e1f59ddb8bb1b6e5ce
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: NONE

No concrete security, correctness, or reliability findings in the authorized PR range. Assistant labels remain separate from public-key-based selection and authorization.

Findings

No concrete security, correctness, or reliability findings were identified.

Notes

  • Review used read-only source and history inspection. Builds and tests were not executed, as requested.

Generated by Codex Security Review |
Requested by: @loganj |
Workflow run

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kyle reviewed the PR and the Nexus review-tracker notes and approved with no comments.

Posted by Codex (gpt-6.1-sol) on behalf of kylez.

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 :bot: Jude’s code review agent — integrated review of fb9c8fc57b5cb10eb3cf5319f50cfb2d432fa159 (base e2dc54e22fc113f298889d76c3acf674cb46db4b). Two author-actionable defects; recommend fixing before merge. Per this repository’s AGENTS.md reviewing policy, this is a COMMENT review with blocking recommendations, not a GitHub Request Changes event.

  1. P2 — Saved, archived targets lose their identity in recovery UI. mobile/lib/features/channels/me/me_chat_assistant.dart:108–119 calls the contextual resolver only for agents still in the live inventory and otherwise displays a cached raw displayName. meAssistantsProvider filters archived agents (mobile/lib/shared/me/me_assistants.dart:35–40) without removing their cached profiles. If a pending MeAgentChange points to archived Atlas A while active Atlas B is offered, the pending notice and Resume/Discard dialog show the indistinguishable label “Atlas”; the persisted new-chat draft makes the same fallback in mobile/lib/features/channels/me/me_chat_draft.dart:21–36. This is especially misleading precisely when the owner must decide how to recover saved intent. IdentityNames.resolve already supports out-of-context keys (mobile/lib/shared/identity_names/identity_names.dart:165–181). Author action: label the saved, unavailable target relative to active choices with an agent role, or give it a stable key-qualified/unavailable label, in both recovery paths; test a cached archived target and an active same-name agent with a persisted pending action. Verification owner: author for fix and regression; reviewer for fresh-head semantics.

  2. P2 — Idle Solo rows repeatedly copy the whole cached profile map on the UI isolate. _MeChatRow watches meWorkingAgentsProvider(channelId) (mobile/lib/features/channels/me/me_page_helpers.dart:35). With any live agents, that provider constructs meAssistantNames before reading whether this chat has typing activity (mobile/lib/features/channels/me/me_working_provider.dart:13–24). meAssistantNameSources copies all cached profiles and the agent-name directory (mobile/lib/shared/me/me_assistant_names.dart:15–38), separately for each expanded Solo row. Thus opening a long Solo list or updating cached profiles performs O(visible chats × cached profiles) allocations even if nobody is working; 500 visible rows × 5,000 cached profiles is 2.5 million copied map entries. This is a source-derived cost, not a measured frame-time claim, and violates VISION_MOBILE.md's responsive-UI/bounded-isolate-work intent. Author action: consult typing before building names and reuse an inventory-scoped computed name context instead of copying the full cache per active row; add a regression that bounds idle-row work and retains distinct labels for simultaneously working namesakes. Verification owner: author for fix/test; reviewer for fresh-head scaling check.

What held: Picker, draft send and switch persistence keep raw pubkeys for identity and membership; the display-label changes do not appear to change their recipient keys in the inspected paths. The owner-inventory name and verified-name precedence are preserved. The exact-head clean worktree passed git diff --check and just mobile-install mobile-check mobile-test (exit 0; full Flutter suite 4,617 passed, 73 skipped; subsequent package suites passed). Relevant Mobile CI was green on the reviewed head. Author-provided Android emulator screenshots demonstrate the normal same-name journey; I did not independently run a native journey or measure UI frame time. A two-simultaneously-working-agents semantics test is still absent from the inspected added tests; that is a coverage/confidence gap, not a third blocking finding. Any new head needs a fresh review of its delta.

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 :bot: Jude’s code review agent — integrated review of fb9c8fc57b5cb10eb3cf5319f50cfb2d432fa159 (base e2dc54e22fc113f298889d76c3acf674cb46db4b). Two author-actionable defects; recommend fixing before merge. Per this repository’s AGENTS.md reviewing policy, this is a COMMENT review with blocking recommendations, not a GitHub Request Changes event.

  1. P2 — Saved, archived targets lose their identity in recovery UI. mobile/lib/features/channels/me/me_chat_assistant.dart:108–119 calls the contextual resolver only for agents still in the live inventory and otherwise displays a cached raw displayName. meAssistantsProvider filters archived agents (mobile/lib/shared/me/me_assistants.dart:35–40) without removing their cached profiles. If a pending MeAgentChange points to archived Atlas A while active Atlas B is offered, the pending notice and Resume/Discard dialog show the indistinguishable label “Atlas”; the persisted new-chat draft makes the same fallback in mobile/lib/features/channels/me/me_chat_draft.dart:21–36. This is especially misleading precisely when the owner must decide how to recover saved intent. IdentityNames.resolve already supports out-of-context keys (mobile/lib/shared/identity_names/identity_names.dart:165–181). Author action: label the saved, unavailable target relative to active choices with an agent role, or give it a stable key-qualified/unavailable label, in both recovery paths; test a cached archived target and an active same-name agent with a persisted pending action. Verification owner: author for fix and regression; reviewer for fresh-head semantics.

  2. P2 — Idle Solo rows repeatedly copy the whole cached profile map on the UI isolate. _MeChatRow watches meWorkingAgentsProvider(channelId) (mobile/lib/features/channels/me/me_page_helpers.dart:35). With any live agents, that provider constructs meAssistantNames before reading whether this chat has typing activity (mobile/lib/features/channels/me/me_working_provider.dart:13–24). meAssistantNameSources copies all cached profiles and the agent-name directory (mobile/lib/shared/me/me_assistant_names.dart:15–38), separately for each expanded Solo row. Thus opening a long Solo list or updating cached profiles performs O(visible chats × cached profiles) allocations even if nobody is working; 500 visible rows × 5,000 cached profiles is 2.5 million copied map entries. This is a source-derived cost, not a measured frame-time claim, and violates VISION_MOBILE.md's responsive-UI/bounded-isolate-work intent. Author action: consult typing before building names and reuse an inventory-scoped computed name context instead of copying the full cache per active row; add a regression that bounds idle-row work and retains distinct labels for simultaneously working namesakes. Verification owner: author for fix/test; reviewer for fresh-head scaling check.

What held: Picker, draft send and switch persistence keep raw pubkeys for identity and membership; the display-label changes do not appear to change their recipient keys in the inspected paths. The owner-inventory name and verified-name precedence are preserved. The exact-head clean worktree passed git diff --check and just mobile-install mobile-check mobile-test (exit 0; full Flutter suite 4,617 passed, 73 skipped; subsequent package suites passed). Relevant Mobile CI was green on the reviewed head. Author-provided Android emulator screenshots demonstrate the normal same-name journey; I did not independently run a native journey or measure UI frame time. A two-simultaneously-working-agents semantics test is still absent from the inspected added tests; that is a coverage/confidence gap, not a third blocking finding. Any new head needs a fresh review of its delta.

…lo rows

A saved agent switch or a resumed new chat can still point to an agent
that is now archived. Its recovery notice, Resume/Discard dialog and
draft header now label it as an agent against the live agents, so it
never reads exactly like a live namesake.

Solo rows no longer build a name context while no agent is working in
them. Working rows read one shared context instead of each copying the
profile cache.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj

loganj commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Both findings held up and are fixed in 6bd0894.

  1. Saved, archived targets. A pending agent switch (its notice and the Resume/Discard dialog) and a new chat waiting to be resumed now label a saved agent that is no longer offered as an agent against the live agents, via IdentityNames.resolve. An archived Atlas with a live Atlas now reads "Atlas" plus a short key suffix, not plain "Atlas". The header test now saves a switch to the archived agent and checks the notice, tooltip and dialog; a new test resumes a new chat for an archived agent with a live namesake. Both fail without the fix.
  2. Idle Solo rows. meWorkingAgentsProvider now checks typing first and returns without reading any names when no agent is working. Working rows read one shared meAssistantNamesProvider instead of each copying the profile cache. A new provider test checks that idle rows build no name context and that two working namesakes read differently (the two-working-agents case you noted as a gap). It fails without the fix.

flutter analyze is clean and the full mobile suite passes (4,619 passed, 73 skipped) at 6bd0894.

@github-actions github-actions Bot removed the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 10, 2026
@loganj
loganj deployed to codex-review November 10, 2026 19:05 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 10, 2026

This branch was successfully deployed

1 active deployment
codex-review — 6bd08944 Deployed Nov 10, 2026 by loganj via Run Codex Security Review #14290
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants