Repository navigation
Bring synced Solo chats and projects to mobile - #8337
Conversation
Signed-off-by: kenny lopez <[email protected]>
Signed-off-by: kenny lopez <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 04892e721f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| sync.check(); | ||
| final events = await session.queryRelay([ | ||
| NostrFilter( | ||
| kinds: const [0], | ||
| authors: pubkey == null ? null : [pubkey], | ||
| limit: pageSize, | ||
| extensions: {'page': page}, |
There was a problem hiding this comment.
Scope assistant discovery instead of scanning every profile
Opening Me starts this unbounded page loop over every kind-0 profile in the community, even though only profiles whose auth tag names the current owner can qualify. In communities with thousands of humans and agents this performs hundreds of sequential HTTP queries and signature checks before the assistant picker or sending can work; query by the owner tag and impose a page/result bound rather than draining the entire directory.
AGENTS.md reference: AGENTS.md:L240-L248
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
🤖 Partially addressed in e707134: discovery is capped at 10 pages / 1,000 profiles, oversized responses are rejected, repeated event verification is skipped, and automatic provider retries are disabled. Selected-agent revalidation remains author-scoped. Owner-scoped discovery is still OPEN: POST /query parses nostr::Filter, whose generic tag deserializer recognizes only one-letter tags and silently discards #auth. Sending #auth would still scan the community. A proper owner-scoped query needs relay support; I have not introduced a client query that falsely claims that scope. The bounded-request regression covers the current fallback.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Signed-off-by: kenny lopez <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab821d167f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
:bot: Jude’s code review agent — REQUEST CHANGES
Reviewed base 2f79374f4867035c33dace91c683440e7e011826 → exact head ab821d167ffc49865d27ef29718996fe717b7203. The integrated systems and product review found seven author-actionable defects:
-
P1 — Me history actions mutate an unwatched provider. The page renders
meMessagesProvider(mobile/lib/features/channels/channel_detail_page.dart:288-289), but_MessageListreconstructschannelMessagesProvider(channelId).notifierfor history pagination, unread-boundary loading, and oldest-message jumps (mobile/lib/features/channels/channel_detail_page/message_list.dart:92,529-535). In a Me chat beyond the initial window, those actions extend the ordinary timeline while the visible Me timeline remains stuck.- Author action: inject/use the selected Me notifier for every history action; add >200-event, oldest-unread, and oldest-jump Me regressions.
-
P1 — a superseded ambiguous save can permanently wedge Me sync. Startup retains and retries the older signed pending event (
mobile/lib/shared/me/me_sync.dart:172-190) even after another device advances the remote head, with no safe superseded-head recovery. The current test codifies rejection after restart rather than convergence.- Author action: detect a definitively newer competing head and provide safe merge/rebase or explicit discard recovery; prove restart convergence without losing newer remote or local user state.
-
P1 — assistant discovery sequentially drains the community-wide profile directory.
mobile/lib/shared/me/me_assistants.dart:43-81pages through every kind-0 profile with no page/result bound rather than querying owner-scoped candidates. Large communities can make picker readiness impractical.- Author action: use owner-tag-scoped discovery with an explicit page/result bound; add a large-directory regression proving bounded requests and work.
-
P2 — optional Me state gates ordinary search navigation.
_openSearchChannelawaitsmeGroupsProvider.future(mobile/lib/features/search/search_page.dart:765-788), so malformed or unavailable Me sync prevents opening a known ordinary channel/message result.- Author action: use cached placement when available and fall back to ordinary routing on Me-specific failure; test ordinary results under malformed and unavailable Me state.
-
P1 — new-Me-chat creation is not crash-durable before relay side effects. Channel creation and member writes occur before any durable creation journal (
mobile/lib/features/channels/me/native_new_me_chat.dart:102-124). Process death can orphan relay state, lose UUID/name/assistant intent, and permit duplicate retry creation, contrary to the recovery contract inVISION_MOBILE.md:12,27.- Author action: journal intent before the first relay write, then resume idempotently or clean up deterministically; add process-death tests at every side-effect boundary.
-
P1 security/product trust — mobile NIP-OA verification is weaker than desktop. Mobile validates condition syntax and signature, returns the first valid
authtag, and cannot evaluate signedkind=/created_atclauses against the profile event (mobile/lib/shared/crypto/nip_oa.dart:20-54). Desktop requires a valid kind-0 event, exactly one auth tag, and event-aware condition evaluation (desktop/src-tauri/src/nostr_convert.rs:58-100). An inapplicable or ambiguous authorization can therefore qualify an assistant for a private Me chat.- Author action: pass the complete profile event, enforce desktop-equivalent cardinality and all signed conditions during discovery and send-time revalidation, and test duplicate auth plus mismatched kind/time conditions.
-
P2 accessibility — locked/empty native iOS assistant rows claim to be actionable. Selection rejects locked or empty states (
mobile/ios/Runner/NativeProfileTextEditor.swift:504-515), but rendering always adds the.buttonaccessibility trait (mobile/ios/Runner/NativeProfileTextEditor.swift:566-578). VoiceOver announces an action that cannot occur.- Author action: remove the button trait and expose disabled/not-enabled semantics in both states; add accessibility assertions and validate with VoiceOver.
Exact-head Mobile, Mobile Swift-domain, client-result, and security checks are green. Focused local Flutter execution was blocked before tests ran by the reviewer environment’s objective_c 9.3.0 Apple SDK discovery failure (Bad state: No element); this is a confidence gap, not requested author rework. Physical-device iOS/Android observation and a full mobile release build also remain verification gaps, not additional defects.
Please fix all seven findings and request a fresh review at the new immutable head.
Signed-off-by: kenny lopez <[email protected]>
Signed-off-by: kenny lopez <[email protected]>
…nt-sessions Signed-off-by: kenny lopez <[email protected]> # Conflicts: # mobile/lib/features/channels/channel_detail_page/message_list.dart
Signed-off-by: kenny lopez <[email protected]>
|
🤖 Pushed Discovery is partially addressed with a 10-page / 1,000-profile bound, oversized-response protection, and no automatic retry loop. Its thread remains open: the relay parses standard Validation: full mobile pre-push checks passed after the merge; focused recovery/security/search/history regressions passed; the production UIKit row passed all locked/empty accessibility combinations on an iOS simulator. No new physical-device VoiceOver pass is claimed. Hosted CI is still running. Ready for re-review of the completed fixes and the remaining discovery limitation. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e707134442
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — REQUEST CHANGES
Reviewed base b77ba862627c7362a4b863a0a077677d811a7558 → exact head e7071344427cf5b12a39e8f49f93a9c6d6a0b216. The previous seven findings were re-reviewed rather than carried forward. Six are resolved or reduced to a bounded documented limitation, but integrated exact-head review found three new author-actionable P1 product defects:
-
Me routing is lost outside the Me tab and placement-aware Search path.
ChannelDetailPage.personalChatdefaults to false and controls the flat timeline, Me message provider/sender, assistant controls, placement check, ownership revalidation, and selected-assistantptag (mobile/lib/features/channels/channel_detail_page.dart:256-302,639-642,702-723,807-812,913-935). Deep links, Activity messages/drafts/reminders, channel links, Channels, quick actions, profile-launched conversations, and Pulse constructChannelDetailPagewithout resolving Me placement (mobile/lib/features/channels/deep_link_dispatcher.dart:144-155;mobile/lib/features/activity/activity_page.dart:264-325;mobile/lib/features/channels/channel_link_navigation.dart:33-36;mobile/lib/features/channels/channels_page.dart:291-296;mobile/lib/features/channels/channels_page/quick_actions_launcher.dart:70-75;mobile/lib/features/profile/user_profile_sheet.dart:48-53;mobile/lib/features/pulse/note_card.dart:240-247). The same synced Me chat therefore becomes an ordinary threaded channel on those routes, hides assistant controls, and can send without Me recipient revalidation/tagging.- Author action: centralize placement-aware channel opening or resolve placement in the destination, then cover notification/deep-link, Activity, channel-link, and at least one secondary route at the production seam.
- Existing inline: #8337 (comment)
-
One permanently failing creation journal can block all future Me chat creation on that installation.
MeChatCreation.saverejects every different intent while a journal exists, and clears the journal only after channel discovery/creation, membership, placement, and journal removal all succeed (mobile/lib/shared/me/me_chat_creation.dart:36-84). Both retry surfaces restore and lock the saved name/assistant, but offer only Retry or dismissal; dismissal does not discard or reconcile the journal (mobile/lib/features/channels/me/new_me_chat.dart:7-17,49-74,114-157;mobile/lib/features/channels/me/native_new_me_chat.dart:8-14,38-80). Revoked agent ownership or permanent relay rejection can therefore wedge New chat forever.- Author action: add an explicit reviewed discard/reconcile path, define safe handling for every partially created channel prefix, and prove permanent rejection → discard/reconcile → different chat creation without orphaning or duplicating state.
- Existing inline: #8337 (comment)
-
Me organization has no last-known-good offline snapshot.
meGroupsProviderstarts from live subscribe/head, emits an error when the session is unavailable, and never persists or restores fetched organization state (mobile/lib/shared/me/me_sync.dart:15-85,93-135). On offline relaunch, previously fetched chats/projects disappear behind Retry; the current widget test explicitly expects that failure surface (mobile/test/features/channels/me/me_page_test.dart:310-326). This contradicts the offline-first and cached-channel-list contract inVISION_MOBILE.md:3,10-13,47.- Author action: persist an encrypted, account- and community-scoped last-known-good snapshot; emit it immediately and refresh in the background; test offline relaunch, scope isolation, corruption, and live replacement ordering.
- Existing inline: #8337 (comment)
Prior-finding reconciliation
- Me history now carries the selected provider/notifier through status, deep-link retention, timeline summaries, pagination, and message-list actions (
channel_detail_page.dart:280-293,515,563,639-642,807-810), with long-history regressions. - Superseded pending Me saves now retain their base and rebase non-conflicting local edits; conflicts remain durable for explicit review/discard (
me_sync.dart:178-289;me_sync_test.dart:131-225). - Search now uses cached placement and a bounded ordinary fallback (
me_search_placement.dart:4-19). - Creation intent is journaled before relay mutation and replayed idempotently across side-effect boundaries (
me_chat_creation.dart:19-85;me_chat_creation_test.dart:9-104), but finding 2 remains the missing terminal recovery path. - Profile admission now validates the complete signed kind-0 event, exactly one
authtag, and applicablekind/created_atclauses (nip_oa.dart:8-33,47-110). - Locked/empty native iOS agent rows expose
.notEnabled, not.button, with the production controller exercised across all four state combinations (NativeProfileTextEditor.swift:566-577;MeAgentAccessibilityTests.swift:5-28). - Assistant discovery is now finite at 10 × 100 profiles, rejects oversized/repeating pages, disables automatic retry, and fails visibly (
me_assistants.dart:14-20,38-94). The relay’s inability to filter multi-letter#authtags remains a documented scale limitation, not an additional merge blocker now that work is bounded.
Exact-head validation
- Live PR and clean local checkout both matched base
b77ba862627c7362a4b863a0a077677d811a7558/ heade7071344427cf5b12a39e8f49f93a9c6d6a0b216immediately before submission;git diff --check b77ba862...HEADpassed. - Exact-head CI completed successfully for Mobile checks, all three Flutter test shards, Android, aggregate Mobile, Mobile Swift Release, Semgrep, zizmor, and DCO. No failing check was observed. At the final freshness check, Mobile Swift Native and Codex security review were still running.
- A reviewer-local scoped Flutter command did not reach test execution because
objective_c-9.3.0invokedxcrunon a host whose Xcode license is not accepted. This is a reviewer/tooling confidence gap, not author rework. - Source and widget evidence corroborate the three failures. No physical VoiceOver/native listening pass or causal mutation run was completed. The author reports a physical-iPhone release journey and simulator accessibility check; those do not exercise the three remaining restart/routing/recovery failures.
Verification owner after the fixes: code review owns source, production-seam route tests, persistence/isolation tests, and causal recovery mutations; mobile product review owns notification/Activity/channel-link journeys, offline relaunch, and native recovery UX. CI/release owns the two checks that were still in progress.
Residual risk: any new head invalidates this verdict. The bounded community-wide assistant scan may still fail to find assistants beyond 1,000 profiles until relay owner-tag filtering exists, but it terminates visibly and does not create an unbounded client loop.
Cleanup: exact-head review checkout and scratch/build artifacts will be removed after this verdict is published; no app/login/keychain state was used.
Signed-off-by: kenny lopez <[email protected]>
|
🤖 Jude’s latest review targets
Each thread has a detailed reply and is resolved. Full mobile pre-push checks passed, including analysis and the full Flutter suite; the channel-detail/recovery suite passed 344 tests. The new follow-ups have not been reinstalled on the physical device. Re-review was requested on the current head. The bounded agent-discovery limitation remains documented and its thread remains open; Jude’s review explicitly treats it as non-blocking now that work is bounded. No additional code changes or empty commit were needed to address this review. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da0b822404
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: REQUEST CHANGES
Reviewed: b77ba862627c7362a4b863a0a077677d811a7558..da0b82240484dcc32c9aed73ea632ee7947258ee (exact live head da0b82240484dcc32c9aed73ea632ee7947258ee)
Risk: high — mobile Solo routing, encrypted account/community-scoped persistence, durable mutation recovery, and user-visible offline behavior.
The previous three blockers were re-reviewed rather than carried forward. All-route Solo placement, chat-journal discard/recovery, and encrypted last-known-good organization snapshots are resolved. One author-actionable defect remains.
Blocking finding
[P1] Offline or interrupted New project creation loses the accepted intent before it becomes durable. Cached Solo state makes New project actionable (mobile/lib/features/channels/me/me_page.dart:61,85-103). After the naming sheet returns, the code generates a UUID and immediately calls MeSync.update() without persisting either the reviewed name or stable ID (me_page.dart:88-102). update() checks connectivity before any journal write (mobile/lib/shared/me/me_sync.dart:235-243) and performs relay head() reads before applying the change closure (me_sync.dart:250,283-300); its pending write occurs only later, after those reads and signing (me_sync.dart:323-329). Offline, the operation therefore throws before retaining the project intent, while perform() reduces the failure to a snackbar (me_page.dart:64-81). Retrying starts with a blank form; process death in the same prefix has the same loss. The exact-head automated inline independently identifies this path: #8337 (comment).
This contradicts VISION_MOBILE.md:10-12,27: accepted user input must survive interruption and offline-first flows must not silently discard it.
Author action: persist an encrypted account- and community-scoped New project intent containing the stable UUID and reviewed name before the first network dependency. On restart expose truthful Resume/Later/compare-and-delete Discard recovery. Add production-seam tests for offline/interruption → restart/resume and stale-intent-safe discard; mutation-removing the pre-network persist must fail them.
Verification owner: author for the persistence/recovery implementation and causal tests; reviewer for exact-new-head ordering/scope/idempotency; Mobile CI for the affected gates.
Prior-blocker reconciliation
- All-route Solo placement: fixed at the destination.
placement_route.dart:23-44resolves scoped organization before constructing the page; provider/send behavior follows the resolved value (channel_detail_page.dart:281-303), and unknown placement remains readable but send-blocked (channel_detail_page.dart:921-949). The default-entry regression covers assistant UI, inline replies, and selected-provider deep-link hydration (channel_detail_page_test.dart:10244-10301). - Chat journal recovery: fixed. The gate runs before either platform flow and offers Later / Discard setup / Resume (
native_new_me_chat.dart:8-38). Discard compare-checks the reviewed scoped intent while preserving partial channel/messages (me_chat_creation.dart:59-70). Unit/widget tests cover stale-intent rejection, offline discard, different-intent creation, and Resume/Discard after restart (me_chat_creation_test.dart:38-68;me_page_test.dart:227-319). - Offline organization snapshot: fixed in source and causal tests. The encrypted cache binds community + pubkey, retains the signed event for replacement ordering, re-verifies the account/coordinate/signature/encrypted envelope, refuses older/null replacement, emits cached state before network work, and retains it on refresh/subscription failure (
me_sync.dart:15-100,137-177,195-212). Tests cover ciphertext-at-rest, account/community isolation, offline relaunch, corruption recovery, empty state, stale/null rejection, and cached-first live replacement (me_sync_test.dart:78-219).
Validation and confidence gaps
- Exact clean local checkout and live PR matched the coordinates above immediately before submission;
git diff --check b77ba862...HEADpassed. - Exact-head Mobile Checks, all three Flutter shards, Android, aggregate Mobile, Client Results, Mobile Swift Release, Semgrep, zizmor, and DCO are green. At final freshness, Mobile Swift Native and Codex Security Review were still running; no required failure was observed.
- Focused local Flutter execution did not reach tests because the reviewer host’s
objective_c 9.3.0native-assets SDK discovery failed withBad state: No element. That is reviewer/tooling debt, not additional author action. - No physical offline-restart/native accessibility journey or causal mutation was run. The Later branch lacks a direct widget assertion. These are confidence gaps, not separate blockers.
Residual risk: project names/IDs can be lost precisely in the cached-offline flow this head introduces. Any new head expires this verdict.
Signed-off-by: kenny lopez <[email protected]>
Signed-off-by: kenny lopez <[email protected]>
|
🤖 Follow-up fixes pushed in 5fc82e4: chat recovery reconciles a confirmed-but-retained Solo save journal before clearing the outer intent, and typing close retries reset only after sustained health rather than subscription admission. Both new Codex threads have regression coverage, replies, and resolution. All 36 focused tests and the full mobile pre-push checks passed. Jude approved the preceding head; requesting fresh review of this small follow-up. Hosted CI remains a separate gate, and the relay owner-discovery limitation stays open. @codex review |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: APPROVE
Reviewed: d61fe2071d2b3d8e56dd931947f16bfe0d6173fe..5fc82e4ba7ed980ac130c2969aa2071bf42e6172 (new-head delta independently re-reviewed from previously approved a2cfb8a912a50f69c640daeaf062c372b08dbf63)
Risk: high — messaging lifecycle, bounded retry behavior, and durable Solo creation recovery
Behavior/contracts traced: terminal typing-subscription close → bounded retry → sustained-health/transport reset; confirmed placement with a retained inner Solo-save journal → cleanup reconciliation → outer-intent clearing; identity/generation/disposal fences and causal regression coverage.
Findings: no unresolved author-actionable defect. The retry budget now survives mere re-admission, retries at bounded 1/2/4-second intervals, exhausts after the fourth terminal close, and resets only after eight uninterrupted healthy seconds or a transport-generation rebuild (mobile/lib/features/channels/channel_typing_provider.dart:43-127). The regression exercises exhaustion, reconnect reset, and sustained-health reset (mobile/test/features/channels/channel_typing_provider_test.dart:93-134). Chat completion now re-enters placement reconciliation when placement is already confirmed but the inner journal remains (mobile/lib/shared/me/me_chat_creation.dart:100-113); false/throwing cleanup coverage proves staged clearing and one relay write (mobile/test/shared/me/me_sync_test.dart:129-169).
Author action: none.
Verification owner: CI/release gate for the still-running Mobile Swift Native and Codex Security Review jobs; reviewer/device tooling for any additional exact-head physical-device and accessibility journey.
Validation: at clean exact HEAD 5fc82e4ba7ed980ac130c2969aa2071bf42e6172, git diff --check d61fe2071..HEAD passed and . ./bin/activate-hermit && just mobile-check passed (754 files format-clean; flutter analyze reported no issues). Hosted Mobile Checks, Flutter test shards 1/3–3/3, Android, Mobile aggregate, Swift Release, Semgrep, zizmor, and DCO were green at the final pre-submit check.
Manual/native evidence: no new exact-head physical-device journey was independently observed. That is a confidence gap, not author rework.
Residual risk: the two named hosted jobs were still in progress at pre-submit. Any new head or PR-caused required-gate failure invalidates this approval.
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: APPROVE
Reviewed: d61fe2071d2b3d8e56dd931947f16bfe0d6173fe..5fc82e4ba7ed980ac130c2969aa2071bf42e6172 (exact head 5fc82e4ba7ed980ac130c2969aa2071bf42e6172)
Risk: high — mobile entry routing, long-lived relay lifecycle, and durable Solo setup journals can silently misroute conversation state or strand recovery if their ownership and retry contracts are wrong.
Behavior/contracts traced: ordinary/Solo chat selection; bounded terminal typing-close recovery; transport-generation and disposal fencing; retained Solo-save reconciliation after confirmed placement; false/throwing cleanup, restart, replacement identity, and community-scope behavior; single relay publication; causal regressions; integration with the previously approved mobile routing and persistence changes. Independent systems/integration and product/UI adversarial passes converged on the same result.
Findings: no unresolved author-actionable defect remains.
- Typing recovery now preserves the consecutive terminal-failure budget across short re-admissions, uses bounded 1/2/4-second retries, stops after the fourth terminal close, and resets only after eight uninterrupted healthy seconds or a transport-generation rebuild. Generation/disposal checks fence stale timers and callbacks (
mobile/lib/features/channels/channel_typing_provider.dart:43-57,66-127). The regression covers exhaustion, reconnect reset, and sustained-health reset (mobile/test/features/channels/channel_typing_provider_test.dart:93-134). - Confirmed Solo placement with a retained inner save journal re-enters idempotent
place()cleanup instead of reporting completion with durable work stranded (mobile/lib/shared/me/me_chat_creation.dart:101-113). False and throwing cleanup regressions prove staged journal retention/clearing and exactly one relay placement write (mobile/test/shared/me/me_sync_test.dart:132-169); surrounding coverage exercises restart, replacement identity, disposal, and community fencing. - The new-head delta introduces no misleading user-visible success/failure state or accessibility regression found by source review.
Author action: none.
Verification owner: the named hosted CI/security gates own completion; reviewer/tooling owns any future exact-device lifecycle and accessibility observation. Any PR-caused required-gate failure should reopen this approval.
Validation at matching head: clean detached checkout; local and live PR heads both matched 5fc82e4ba7ed980ac130c2969aa2071bf42e6172 immediately before submission. Review-lane validation reported just mobile-check passing (Dart format over 754 files plus Flutter analysis). just mobile-test passed its gateway-recipe checks but could not start Flutter tests because this host has not accepted the Xcode license (xcrun exit 69). Hosted Mobile Checks, all three Mobile test shards, Android, aggregate Mobile, Mobile Swift Release, Semgrep, zizmor, and DCO were green at submission.
Manual/native evidence: no fresh exact-head device or accessibility journey was observed; no native behavior claim is made.
Residual risk: Mobile Swift Native and Codex Security Review were still running at submission. The local Xcode-license failure and absent device observation are confidence gaps, not author rework. Any head movement invalidates this verdict.
Signed-off-by: kenny lopez <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92d88ecf96
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: REQUEST CHANGES
Reviewed: fea02d174d0762b18b73138abc833d7c140f9718..92d88ecf96a1a25fd667c4e763c9f33c47bf27c2 (exact head 92d88ecf96a1a25fd667c4e763c9f33c47bf27c2)
Risk: high — mobile unread truth, relay catch-up/lifecycle, offline persistence, and the merged iOS notification-extension boundary.
Behavior/contracts traced: Solo organization/routing/recovery and unread state; read-state initialization through relay catch-up, Home filtering, row/section/tab badges; merge resolution with #8369's notification enrichment, native avatar/cache/download/auth boundaries, and Flutter push-cache removal.
Blocking finding
- [P2] Fresh read state falsely marks pre-existing Solo history unread.
ChannelsNotifier._catchUpUnreadEventsincludes all active member channels and, when no read marker exists, fetches from timestamp zero (mobile/lib/features/channels/channels_provider.dart:603-624) and records qualifying historical events as unread (:647-668). Ordinary Home channels avoid exposing that bootstrap history by seeding null read marks tolastMessageAtand gating unread presentation until seeding completes (mobile/lib/features/channels/channels_page/body.dart:151-195). But Solo IDs are filtered out before_ChannelsBody(mobile/lib/features/channels/channels_page.dart:223-227), whilemeUnreadChannelIdsProviderdirectly exposes computed Solo unread (mobile/lib/features/channels/me/me_unread_provider.dart:8-20) to the app-level tab and Solo rows/sections. On a fresh install/profile, existing Solo conversations therefore appear newly unread until opened. Existing coverage starts with a pre-seeded Solo marker (mobile/test/features/channels/me/me_unread_test.dart:65-72), so it misses this path.
Author action: give active Solo channels the same per-identity initial seeding/display-gating contract as ordinary channels, without hiding genuinely newer events. Add a deterministic ready-but-empty read-state regression proving historical Solo messages do not badge during initialization and a later event does; mutation-check the seed/gate.
Verification owner: author + Flutter CI; reviewer to inspect catch-up/readiness ordering on the next exact head.
Validation
At clean 92d88ecf96a1a25fd667c4e763c9f33c47bf27c2:
git diff --check 5fc82e4ba7ed980ac130c2969aa2071bf42e6172..HEAD— pass.git diff --check fea02d174d0762b18b73138abc833d7c140f9718..HEAD— pass.just mobile-check— pass: 748 files format-clean; Flutter analyze clean.just mobile-test— gateway recipe checks passed; Flutter execution could not start because this review host has not accepted the Xcode license.- Exact-head hosted Mobile Checks, all three Flutter test shards, Android, Mobile aggregate, Mobile Swift Native and Release, Desktop RC, Semgrep, zizmor, and DCO — success at submission. Codex Security Review remained in progress.
Both independent systems/integration and product/UI/adversarial passes confirmed the unread defect and found no additional author-actionable issue in the merge delta.
Manual/native evidence: no fresh exact-head physical-device observation. Local Swift/Flutter native execution was blocked by reviewer Xcode-license state; that is reviewer tooling debt, not another author action.
Residual risk: exact-head device behavior for enriched Solo notification avatars and extension deadline/network failure remains owned by native/release validation. Any new head invalidates this review.
Signed-off-by: kenny lopez <[email protected]>
|
🤖 Addressed Jude’s fresh-profile Solo unread finding in 72466da. Active Solo chats now receive the same initial lastMessageAt baseline as Home before unread is exposed. Baselines are captured before deferred writes so a newer event during initialization stays unread; identity/community/readiness boundaries reset initialization. Deterministic ready-but-empty tests cover multiple chats, historical catch-up, new events during/after initialization, and identity changes; existing row/section/tab behavior still passes. Removing the seed fails the baseline regression, and removing the gate exposes the false unread state. Full mobile formatting, analysis, file-size checks, and Flutter tests passed in the pre-push gate. The matching inline thread is replied to and resolved; hosted CI and renewed approval remain separate gates. The documented relay discovery limitation stays open. @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72466da975
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: REQUEST CHANGES
Reviewed: fea02d174d0762b18b73138abc833d7c140f9718..72466da975f7b27e2125f8101fb9f21b20b5464d (exact head 72466da975f7b27e2125f8101fb9f21b20b5464d)
Risk: high — mobile unread truth crosses channel discovery, relay catch-up, read-state initialization, identity/community lifecycle, and three badge surfaces.
Behavior/contracts traced: fresh Solo history seeding and display gating; channels-ready/read-state-not-ready ordering; events before and after baseline capture; identity/community reset; monotonic read-marker ownership; row, collapsed-section, and tab presentation. The systems/lifecycle pass initially cleared the races after readiness; the product/adversarial pass and the exact-head inline finding exposed an earlier false→true readiness ordering that those tests do not exercise.
Blocking finding
- [P2] A genuinely new Solo reply can be permanently seeded as read when channels become available before read-state readiness.
MeUnreadChannelIds.buildreturns whilereads.isReadyis false before it records any channel baseline (mobile/lib/features/channels/me/me_unread_provider.dart:19-25). The seed holder also watches(pubkey, isReady), so it is recreated when readiness becomes true (:75-80). If Solo channels load with old history, then a live reply advanceslastMessageAtbefore read-state initialization finishes, the ready build first captures that already-advanced timestamp (:34-40) and seeds it (:45-56). The new reply is therefore treated as read and never lights the Solo row, collapsed section, or tab. The added regressions construct read state ready from its first build (mobile/test/features/channels/me/me_unread_test.dart:72-83,109-179); they prove the later “reply after baseline capture” race, not this distinct ordering. This matches the exact-head inline finding: #8337 (comment)
Author action: capture each initial active Solo channel timestamp as soon as the channel snapshot plus identity/community scope are known, preserve that baseline across the same scope’s false→true read-state transition, and defer only marker writes and badge exposure. Add a deterministic regression where read state starts not ready, channels expose old history, a newer reply arrives, then read state becomes ready: old history stays unbadged and the newer reply badges at row, collapsed-section, and tab surfaces. Mutation-check moving baseline capture back below readiness.
Verification owner: author + Flutter CI, followed by exact-head reviewer inspection.
Validation
At clean local HEAD 72466da975f7b27e2125f8101fb9f21b20b5464d:
git diff --check 92d88ecf96a1a25fd667c4e763c9f33c47bf27c2..HEAD— pass.git diff --check fea02d174d0762b18b73138abc833d7c140f9718..HEAD— pass.just mobile-check— pass: 748 files unchanged by formatting; Flutter analysis found no issues.just mobile-test— gateway recipe checks passed; Flutter execution then could not start because this reviewer host’sobjective_cnative-assets hook received emptyxcrun/SDK output.- Exact-head hosted Mobile Checks, all three Flutter test shards, Android, aggregate Clients Results, aggregate Mobile, Mobile Swift Release, Semgrep, zizmor, and DCO were green. Mobile Swift Native and Codex Security Review remained in progress immediately before submission.
Manual/native evidence: no fresh exact-head device or accessibility journey was observed; no physical-device claim is made.
Residual risk: the local Apple toolchain failure, pending hosted Swift/security gates, and absent device observation are confidence gaps owned by reviewer tooling/CI/native verification, not additional author rework. Any head movement invalidates this verdict.
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: REQUEST CHANGES
Reviewed: fea02d174d0762b18b73138abc833d7c140f9718..72466da975f7b27e2125f8101fb9f21b20b5464d (exact head 72466da975f7b27e2125f8101fb9f21b20b5464d)
Risk: high — mobile unread truth, identity/community-scoped initialization, relay catch-up ordering, and row/section/tab badge state.
Blocking finding
- [P2] A genuinely new Solo reply can be permanently seeded as read when channels become available before read-state readiness.
meUnreadChannelIdsProviderreads the channel snapshot but returns whilereads.isReady == false, before recording any baseline (mobile/lib/features/channels/me/me_unread_provider.dart:19-25). Its seed holder watches(pubkey, isReady), so the false→true transition creates a new empty seed state (:75-80). The first baseline is then captured from each channel's currentlastMessageAt(:34-40). If a live reply advances that timestamp while read state is still loading, the later seed writes the reply timestamp as read (:45-56), and its row, collapsed-section, and tab indicators never appear. Existing new tests construct read state ready from their first build, so their covered "event during initialization" race begins only after baseline capture and does not exercise this ordering.
Author action: capture each initial active Solo channel timestamp as soon as channel snapshot plus identity/community scope are known, preserve it across the false→true read-state transition, and defer only read-state writes and badge exposure. Add a deterministic regression where reads begin unready, old Solo history is visible, a newer reply arrives, reads become ready, old history remains unbadged, and the newer reply badges at row, collapsed-section, and tab surfaces. Mutation-check moving capture back behind readiness.
Verification owner: author + full exact-head Flutter CI, followed by reviewer inspection of the readiness delta.
Integrated result
Both assigned lanes are integrated. Systems review found the ordinary ready-state seeding path sound: captured baselines survive channel rebuilds, newer existing markers win, empty chats remain eligible for their first event, scope changes reset state, and disposal fences deferred writes. Product/adversarial review found the distinct channels-ready/read-state-unready boundary above; source reinspection confirmed it, so that evidence supersedes the narrower clearance. No additional author-actionable defect was established in Solo routing/recovery or the merged notification/avatar paths.
Validation and confidence gaps
- Exact-head
git diff --checkandjust mobile-checkpassed; formatting covered 748 files and Flutter analysis was clean. - Hosted Mobile Checks, all three Flutter shards, Android, Clients aggregate, Mobile aggregate, and Swift Release are green at review time.
- Local
just mobile-testreached the gateway-recipe pass but Flutter startup was blocked by this reviewer host's unaccepted Xcode license /objective_cnative-assets SDK lookup. Mobile Swift Native and Codex Security Review remained in progress. These are reviewer/CI confidence gaps, not additional author work. - No fresh physical-device observation of notification-extension deadline/network failure was completed; native/release validation owns that residual risk.
Any new head invalidates this review.
Signed-off-by: kenny lopez <[email protected]>
|
🤖 Addressed the readiness-ordering finding in 1d4c413 and resolved the matching inline thread. The initial history baseline now survives read-state initialization, so a newer reply remains unread on the row, collapsed project section, and Solo tab. The deterministic regression passes and fails when baseline capture is moved back behind readiness. Focused tests, mobile checks, and the full pre-push mobile test suite passed. Fresh Jude review requested; hosted CI and approval are pending. The previously documented relay-dependent owner-scoped discovery limitation remains open. @codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Reviewed exact head 1d4c41354564c093d4976f410692c104253c2fb2 against base fea02d174d0762b18b73138abc833d7c140f9718.
The corrective delta closes the Solo unread readiness race at the right boundary: it captures each active chat's initial lastMessageAt before read-state readiness, preserves that baseline across the readiness transition, resets it when pubkey/relay scope changes, and seeds only when no effective marker exists. A reply arriving while reads initialize therefore remains newer than the seeded marker instead of being swallowed.
The production-seam regression covers the unready → reply → ready sequence and verifies marker 20 against reply 21, plus the Solo tab, row, collapsed-project, read, subsequent-reply, thread-read, and forced-unread surfaces. Both independent review lanes found no unresolved author-actionable defect.
Validation on this exact head:
just mobile-checkpassed: formatting unchanged and Flutter analysis reported no issues.git diff --checkpassed; exact-head worktrees were clean.- Hosted Mobile Checks, all three Mobile test shards, Android, Clients Results/Mobile aggregate, Swift Release, DCO, Semgrep, and zizmor passed at review time.
Confidence gaps, not author defects:
- Local Flutter/native execution was blocked before collection by the reviewer host's unaccepted Xcode license /
xcrunSDK lookup. Reviewer tooling/native validation owns this; no author action. - Physical-device/native observation was not repeated for the follow-up. Native/device validation owns this if required.
- Mobile Swift Native and Codex Security Review were still running at the final pre-submit refresh. Those named CI gates own completion; no author action absent a PR-caused failure.
Author action: none.
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent
Verdict: APPROVE
Reviewed: fea02d174d0762b18b73138abc833d7c140f9718..1d4c41354564c093d4976f410692c104253c2fb2 (exact head 1d4c41354564c093d4976f410692c104253c2fb2). The prior clearance at 72466da975f7b27e2125f8101fb9f21b20b5464d was treated as expired; this is a fresh review of the corrective delta plus its affected unread/UI paths.
Risk: high — the change controls virtual unread state across asynchronous read-state initialization, identity/community ownership, persistent marker writes, and three user-visible surfaces.
Behavior/contracts traced: mobile/lib/features/channels/me/me_unread_provider.dart:25-42 now captures the active Solo channel's initial lastMessageAt once identity and organization are known, before readiness can admit a newer reply. Readiness gates only writes/display. :46-64 seeds only a still-missing effective marker, and :76-81 retains the baseline across false→true readiness while replacing it on pubkey or relay/community changes. The production-seam widget regression at mobile/test/features/channels/me/me_unread_test.dart:188-282 drives unready baseline 20 → reply 21 → ready, proves marker 20, then verifies the Solo tab, unread row, collapsed-project indicator, subsequent read/reply/thread-read, and forced-unread behavior. Accessibility semantics remain explicitly owned by the row, section, and tab paths.
Findings: no unresolved author-actionable defect. Independent systems/integration and product/UI review both reached APPROVE; their lifecycle, persistence, event-ordering, row/collapse/tab, and accessibility checks were reconciled into this verdict.
Author action: none.
Verification owner: reviewer/tooling for native observation and the locally blocked Flutter execution; named CI/release gates for checks still running.
Validation at matching clean HEAD:
- PASS — local and live PR head both
1d4c41354564c093d4976f410692c104253c2fb2; merge-base exactlyfea02d174d0762b18b73138abc833d7c140f9718;git diff --checkclean. - PASS — two independent
just mobile-checkruns: 748 Dart files unchanged and Flutter analyzer reportedNo issues found. - PASS — independent
just file-size-check: 10/10 core policy tests and all surface entrypoints. - PASS — exact-head hosted Mobile Checks, all three Mobile test shards, Android, Mobile aggregate, Clients Results, Mobile Swift Release, DCO, Semgrep, and zizmor.
- The author-reported causal mutation (moving baseline capture behind readiness) fails the new regression with expected marker 20 / actual 21; this supports falsifiability but was not independently rerun locally.
- Local
just mobile-testreached five passing gateway-recipe probes, then could not start Flutter collection because this host has not accepted the Xcode license and theobjective_cnative-assets hook received no SDK path fromxcrun. That is a reviewer-environment confidence gap, not evidence of a PR defect.
Manual/native evidence: no new physical-device, iOS visual, or VoiceOver run is claimed for this follow-up.
Residual risk: native observation and an independent mutation run remain outstanding. At submission time, Mobile Swift Domain / Mobile Swift (Native) and Run Codex Security Review were still in progress; the exact-head Codex source review separately reported no major issue. Those external gates—not author rework—own final merge readiness.
## Summary Polish mobile Home and Solo: group Search/Inbox beside a separate profile avatar, move Inbox and Settings to bottom-up pages with Close controls, and place tabs opposite the plus/compose action. Morph plus to compose clockwise when entering Solo and reverse counterclockwise when returning Home, with a directional quarter-turn of the icon inside a level button and a subtle scale pulse, respecting reduced motion. Align Search’s Close control with Inbox, simplify its placeholder, and initialize selected filter contrast correctly. Collapse the plus menu when leaving Home. Center Solo’s native-glass New chat empty state and keep it stationary behind the keyboard. Standardize Tabler icons and neutral backgrounds, retain SF Back/Close on iOS, use native Liquid Glass headers, tab container, and actions (solid grey selected tab, slightly darker on iOS) on iOS and black actions on Android, and smooth the expanded menu corners. Repair native header safe-area/return layout and compose movement after cancellation. ### Related issue None found for this UI refinement; follows #8337. ### Testing - Formatting, analysis, full mobile suite: 3,487 passed, 4 skipped; 3 push-configuration tests passed. File-size checks passed. - Base UI built and installed on Pixel and iPhone in place. Pixel navigation/New chat cancellation checked. Subsequent glass and rotation refinements are previewed on iPhone; motion feel awaits human confirmation. - Draft pending human confirmation of the final Settings entrance/return behavior on iPhone. ### Pixel snapshots | Solo | Expanded actions | | --- | --- | | <img src="https://raw.githubusercontent.com/block/buzz/16dc0f8af971e63d788fee20974997fab925dec9/docs/screenshots/mobile-home-solo/pixel-solo.png" width="280" alt="Solo empty state with New chat" /> | <img src="https://raw.githubusercontent.com/block/buzz/16dc0f8af971e63d788fee20974997fab925dec9/docs/screenshots/mobile-home-solo/pixel-actions.png" width="280" alt="Expanded channel actions with smooth corners" /> | --------- Signed-off-by: kenny lopez <[email protected]> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>



Summary
Bring desktop Solo chats and project organization to mobile, with synced collapsible sections, shared unread highlights and tab badges, agent selection, shared search, native iOS creation sheets, and inline typing bubbles that transition into replies.
Resolve Solo placement for every chat entry route, retain encrypted organization snapshots for offline reading, and let users resume or discard unfinished chat and project setup without deleting existing messages.
Related issue
Related: #6565; expandable agent transcripts remain separate work.
Testing
1d4c41354.just cistopped on the existingbuzz-acptestsignals_cancel_startup_turn_and_pending_stdin_without_hanging(process exit-code assertion); no Rust production changes are included in this PR.Remaining review limitation
Agent discovery is bounded to 10 pages / 1,000 profiles with automatic retries disabled. Owner-scoped discovery remains open because the current relay ignores
#authfilters; supporting that query requires a relay change.Snapshots
Sample data, shared Flutter/Android surface. Native iOS uses platform sheets and controls.