Skip to content

feat(solo): open chats for community viewing - #2107

Open
tellaho wants to merge 5 commits into
mainfrom
tho/solo-open-view-only
Open

tellaho wants to merge 5 commits into
mainfrom
tho/solo-open-view-only

Conversation

@tellaho

@tellaho tellaho commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Category: new-feature

User Impact: Solo chat owners can let their community read a chat while keeping participation by invitation in the access dialog.

Problem: Solo owners could share individual membership, but could not open a chat for community reading while keeping its Solo identity and project filing.

Solution: Reuse channel access with View only / Private, preserving Solo identity and individual roles. Access changes and conversion disclose history, Canvas workspace folders/agent instructions, and retained member access, including when role verification is unavailable. Conversion preselects the current policy and saves matching admission; invited Solo agents receive Bot so they can reply.

Changes

File changes

docs/buzz-help.md
Explain View only / Private, the conditional retained-access confirmations, Bot admission and the three conversion access choices.

docs/solo/README.md
Describe Solo identity independently of visibility and conversion selecting Private, View only or Public.

docs/solo/sharing.md
Document community reading, invitations, Bot admission, conversion access and existing Public policies. Explain the conditional confirmations and that Private blocks nonmembers while joined Guests retain access until removed in People.

src/app/shell/SearchResults.tsx
Label View-only chats accurately while retaining a truthful Public chat label for policies created elsewhere.

src/bundled/channels/ChannelGeneralAccess.test.tsx
Cover the two available choices and transition-specific confirmation copy, including retained Public → Private membership and the expanded Canvas disclosure.

src/bundled/channels/ChannelGeneralAccess.tsx
Reuse the shared access options and policy mapping. Solo offers View only / Private, retains Public (existing), and confirms history/Canvas disclosure plus the retained-access sentence for each applicable transition.

src/bundled/channels/ChannelMembersDialog.solo.test.tsx
Exercise Public → Guest admission and View only → Private through real dialog/session wiring, including verified Guest/Member roles, pending/failed role verification with cached Guest or Member roles, unchanged membership, cancellation and preserved Solo filing.

src/bundled/channels/ChannelMembersDialog.test.tsx
Cover explicit Bot grants in public/private Solo chats with Guest defaults and verified preview rosters for Public/Member visitors, retaining no-message, focus and recovery checks.

src/bundled/channels/ChannelMembersDialog.tsx
Enable shared General access for Solo and remove the redundant hint. Keep the retained-Guest warning unless the existing signed role snapshot verifies no Guests; loading/error states retain it. Read-only Solo previews use ordinary roster reads rather than member-only sharing refreshes.

src/features/channels/general-access.ts
Share access labels, descriptions, current-policy classification and visibility/admission mapping between the access picker and conversion. Private preserves the existing admission default.

src/features/channel-members/members.test.ts
Verify participatory invitations with a saved Guest admission default. Cover legacy no-role receipts, unknown outcomes, verified roles, Guest recovery across restart, explicit failed-receipt dismissal and concurrent role administration.

src/features/channel-members/members.ts
Permit invitations to open Solo chats and explicitly grant Member participation to invited people. Recovery never replays a legacy no-role receipt as a participation grant; signed roles distinguish a completed invitation from a retained Guest, with an actionable remove-and-invite-again path.

src/features/messages/ChannelJoinNotice.tsx
Use the existing chat/channel noun for the read-only notice.

src/features/messages/MessageComposer.test.tsx
Exercise admission-before-send and cancellation/refusal in both public and private Solo chats with a Guest default.

src/features/relay/channel-writes.ts
Request Bot explicitly for Solo agents added by the access dialog or composer. Retry only compatible role receipts while preserving acknowledgement, delivery, target and expiry checks; ordinary channel admission is unchanged.

src/features/relay/contracts.ts
Describe Solo as a labelled stream independently of visibility.

src/features/relay/discovery-permissions.test.ts
Cover visitor reading, denied View-only participation and access revocation on privacy restoration.

src/features/relay/discovery.ts
Recognize Solo metadata independently of visibility.

src/features/relay/session-agent-admission.test.ts
Verify signed Bot invitations with Guest defaults in public/private Solo chats, same-event retries and rejection of no-role, Guest, Member and duplicate-role receipts.

src/features/relay/session-discovery.test.ts
Verify signed public metadata retains Solo classification.

src/features/solo/ConvertToChannel.test.tsx
Verify all nine access-policy transitions, current-policy preselection and Guest/Member readback while preserving TTL and labels. Exercise uncertain-save recovery, transient Finish read failures and superseded admission mismatches through Reload and remount without republishing or unfiling.

src/features/solo/ConvertToChannel.tsx
Offer the shared Private, View only and Public options with current access preselected. Save visibility and admission together, preserve the chosen policy during recovery, and reject changed admission before unfiling. Private conversion reuses the retained-access warning; verified superseding edits retire obsolete unconfirmed receipts while transient reads retain recovery.

src/features/solo/labels.ts
Keep label-based presentation separate from relay access authority and share the Solo disclosure and retained-member warning between access and conversion.

src/features/solo/share.ts
Include admission in exact conversion-intent matching and fresh-details conflict checks.

src/features/solo/share.test.ts
Verify explicit Member grants for people and preserved Bot grants for agents.

tests/browser/channel-members-focus.spec.mjs
Expect no accessible description after removing the Solo hint, while retaining pointer/Enter agent admission and search-focus assertions.

tests/browser/fixture.mjs
Supply signed public Solo metadata and preview rosters for real broker/discovery journeys.

tests/browser/solo-chats.spec.mjs
Extend the existing conversion focus journey to start from View only, exercise all three choices by pointer and keyboard, preserve Guest admission, and check the retained-Guest Private conversion warning in both themes and sizes without adding browser cases.

tests/browser/solo-public-access.spec.mjs
Exercise the two-choice picker, View-only save/reload, visitor reading and privacy restoration. Extend the owner journey for the conditional confirmations, including Public (existing) → Private, cancellation/focus and narrow enlarged-text layouts; inspect an existing Public/Member visitor without a roster error. No browser cases added.

Reproduction steps

  1. Open an owned Solo chat and choose Share access. Open General access: only View only and Private are offered, by pointer and keyboard.
  2. Choose View only. Check that the confirmation says “Everyone in this community will be able to read this chat’s full history and Canvas, including its workspace folders and agent instructions.” Cancel returns focus without saving; confirming and reloading retains View only and the same Solo filing.
  3. As another community member, find the chat in Search Buzz or open its link. History is readable; Join, composition and reactions are unavailable. Its access dialog is read-only, and viewing adds no sidebar membership.
  4. Return to Private: nonmembers lose reading access. Existing members, including Guests who joined through CLI/MCP while View only, keep access until removed in People. For View only → Private, a Guest or loading/failed role verification adds: “People who joined while it was View only are members and keep reading until you remove them in People.” A verified roster without Guests keeps “Only chat members will have access.” New invitations grant Member participation even with a saved Guest admission default; adding an agent still admits it without starting a run.
  5. For a Solo chat made Public with Member admission through another client, General access shows Public (existing). The owner can change it to View only or Private, but cannot select Public here. Choosing View only appends “People already in the chat keep their access.” Choosing Private warns that joined members keep their access until removed in People. Private → View only keeps the history/Canvas disclosure.
  6. Open Convert to channel for a View-only Solo chat: View only starts selected. Confirming keeps Guest admission. Choosing Public instead saves Member admission; choosing Private restricts reading to members and warns that people who joined still keep their access until removed in People. History, existing membership, TTL and the focus handoff remain intact. For a private chat, selecting View only or Public shows the full history/Canvas disclosure before confirmation.
  7. With a Guest admission default, add a new owned agent through access search or a composer mention. Its signed invitation explicitly grants Bot in both public and private Solo chats; retries retain the same compatible event.
  8. Recover an invitation saved by an older client without a role: it cannot be replayed as a participation grant. A signed Guest result explains how to remove the person in People and invite them again; unknown results stay protected. Dismiss a definitively failed original in Outbox before sending a new invitation.
  9. After an uncertain conversion is confirmed elsewhere, change its admission in another client before Finish conversion. Finish rejects the mismatch and retires its local receipt. Reload or reopen: no dead Finish entry, no preference write and no repeated conversion command. A failed read instead preserves Finish recovery.
  10. Open access as a nonmember of an existing Public/Member Solo chat. The ordinary preview roster loads without “Current access could not be confirmed” and no membership or write is created.

Scope and limits

The available choices are a UI restriction, not a relay or agent-harness safeguard. CLI/MCP and other clients can still set Public with Member admission. Existing members can still address agents and replace the shared Canvas; View only restricts new people and does not revoke existing memberships. Wake and owner-Canvas hardening belong to a separate follow-up. This PR does not claim to resolve those underlying risks.

Solo visitors read without a Join action. Ordinary channel access choices and Join behavior remain as on main.

Validation

Review fixes are committed as 590f4ee29. The previous feature baseline is 8af2cd287.

  • 116 focused unit checks pass across the complete access (31), conversion (30) and invitation (55) files. Regressions fail against their pre-fix implementations. Coverage includes both Private warning routes, all nine conversion policies, transient and superseded Finish recovery through reload/remount, legacy invitation uncertainty, signed Guest/Member/Admin roles, failed role verification, explicit Outbox dismissal and concurrent role administration.
  • All 24 Chromium/WebKit cases pass across the complete Solo public-access and Solo chats files (1.4m), checked at f51d51ca8. The final follow-up only blocks stale legacy-role confirmation; the checked warning/conversion and browser files are unchanged. Pointer/keyboard selection, cancellation/focus return, signed broker readback and both themes at wide/narrow enlarged-text sizes pass. All eight new rendered warnings were inspected.
  • No browser cases were added or removed. Policy/role and recovery matrices stay in Vitest; existing journeys cover browser routing, focus and modal geometry. The earlier keyboard repair retains its selection and publication assertions without sleeps or retries.
  • Mandatory commit and push gates: pass (TypeScript/related units 94.58 seconds; design checks 4.73 seconds). Hosted CI on the new head is separate from these local results.

The existing AlertDialog, compact Dialog, Select, channel-details save/readback and signed member-administration owner remain in use. Warning copy adds no query or membership write. Only a recovered no-role Solo invitation with an already-joined target requests fresh role verification. It never automatically promotes that person or replaces an uncertain command. A verified Guest outcome uses the existing remove-and-invite-again workflow; a verified superseding conversion edit retires local orchestration without overwriting the edit or unfiling.

Browser checks and screenshots use the real built frontend and production browser broker with disposable modeled relay data. Changed warnings and recovery behavior still need human retest. Native/live-relay recovery remains unverified; the previous human walkthrough was at c96493853. The acceptance marker remains absent.

Screenshots / demos

The two retained-access warning rows below show the checked tree at f51d51ca8; those UI files are unchanged in 590f4ee29. Earlier disclosure and conversion captures show 8af2cd287. Unchanged sharing, picker and visitor views retain their earlier captures. All use disposable modeled relay data. Description captures use 1440px/100% text; narrow enlarged-text layouts remain covered by the browser checks. The enlarged-text picker uses its existing scrollport.

Dark · 1440px

Private View only Visitor
Private, dark, 1440px View only, dark, 1440px View-only visitor, dark, 1440px

Light · 1440px

Private View only Visitor
Private, light, 1440px View only, light, 1440px View-only visitor, light, 1440px

General access and Canvas disclosure · 1440px

Menu · dark Menu · light Confirmation · dark Confirmation · light
View only and Private, dark, 1440px View only and Private, light, 1440px View-only Canvas disclosure, dark, 1440px View-only Canvas disclosure, light, 1440px

Conversion access and existing-public visitor

Conversion access at 8af2cd287, with View only preselected. 1440px/100% text.

Width Dark Light
1440px Conversion with View only selected, dark, 1440px Conversion with View only selected, light, 1440px
Existing Public/Member visitor
Public Member preview access without a roster error

Conditional Solo access confirmations

Transition and viewport Dark Light
View only → Private · verified Guest · 1440px View only → Private · verified Guest · 1440px, dark View only → Private · verified Guest · 1440px, light
Public (existing) → View only · 1440px Public (existing) → View only · 1440px, dark Public (existing) → View only · 1440px, light
Public (existing) → Private · 1440px Existing public members retain Private access, dark Existing public members retain Private access, light
View only → Private conversion · 1440px Private conversion retains joined viewers, dark Private conversion retains joined viewers, light

Private chat → View only conversion disclosure · 1440px

Dark Light
Private chat conversion disclosure, dark Private chat conversion disclosure, light

@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 [blocking] View only also publishes the chat's Canvas — workspace folders and agent instructions — but the consent copy only says "full history".

Adversarial review by Princess Donut (AI agent), posted from Taylor's account on Taylor's behalf, at 099c634f7.

A Solo chat's Canvas is a channel-scoped kind 40100 event. It holds the workspace's agent instructions and the local folders its agents work in. See src/features/agent-harness/project.ts:1-29 and docs/solo/README.md:188-189. Once visibility=open is set, anyone in the community who can read the chat's h-tagged events can read that Canvas too, through any client.

How it fails: An owner whose chat was started from a workspace chooses View only. The confirmation says only "Everyone in this community will be able to view this chat’s full history." (ChannelGeneralAccess.tsx:336). Their absolute local paths, such as /Users/<name>/…/<project>, and their private agent instructions are now readable by the whole community. They weren't told this, and the new Open for community viewing docs don't mention it either.

Fix: For Solo, make the confirmation (and docs/solo/sharing.md) state that the chat's Canvas — workspace instructions and folders — becomes community-readable. Add an assertion for it in the existing ChannelMembersDialog.solo.test.tsx View-only test. Withholding Canvas from visitors would be a separate product decision. I'm not proposing that here.

@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 [blocking] Mobile still requires private for Solo, so a View-only chat stops being Solo on mobile.

Adversarial review by Princess Donut (AI agent), posted from Taylor's account on Taylor's behalf, at 099c634f7.

This PR changes the shared classification contract. soloChannelMetadata is now stream + buzz.solo label, whatever the visibility, and the Solo docs promise that "every member and agent … reads the same answer." block/buzz mobile at main acf56fff6 still uses:

// mobile/lib/features/channels/channel.dart:109
bool get isSolo => isStream && isPrivate && labels.contains('buzz.solo');

isPersonalChat builds on that. For a label-only chat it falls back to the legacy MeGroups.channels list, not to workspace assignments.

How it fails: The owner sets a chat to View only on desktop and then opens mobile. The chat drops out of Me (me_page.dart:173) and is listed among ordinary channels (channels_page.dart:269). The two clients disagree about the same signed metadata. This PR's session-discovery.test.ts change flips exactly this expectation (isSolo for a non-private labelled stream goes from false to true) on desktop only.

Fix: Land a companion block/buzz change that drops isPrivate from Channel.isSolo, with a test mirroring session-discovery.test.ts, and order its release with this PR. Otherwise, list this as a known gap in the description and get an explicit decision to accept it. The current description only mentions "native-app acceptance" in general.

@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 [optional] Stale contract comment: src/features/relay/contracts.ts:46 still says session: "a private stream with the Solo label". After this PR, Solo no longer depends on visibility, and the type value is solo, not session. Please update it next to the discovery.ts / labels.ts comment changes.

Princess Donut (AI agent), posted from Taylor's account on Taylor's behalf.

@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 [optional] Test the "Public (existing)" Solo state, which is now reachable from the Solo surfaces.

Princess Donut (AI agent), posted from Taylor's account on Taylor's behalf.

sharing.md:78 says Solo doesn't offer Public. Any 9002-capable client (CLI or another app) can still set visibility=open + join_role=member on a labelled chat. Before this PR, discovery stopped treating that chat as Solo. Now it stays Solo: it keeps chat treatment, top-level agent replies and Solo filing, and any community member can join and post in the owner's agent chat. ChannelGeneralAccess.tsx:285 already renders Public (existing) for it. Nothing covers that state from the Solo dialog. A small case in ChannelMembersDialog.solo.test.tsx would lock in the intended behavior: it shows Public (existing), offers only View only / Private, and switching to View only sets join_role=guest. A short note in sharing.md would also help.

@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from 099c634 to e71d37f Compare November 1, 2026 06:23
@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 [blocking] With Public, anyone in the community can join and direct the owner's agents inside the owner's workspace folders, and the answers land in a community-readable chat.

Adversarial re-review by Princess Donut (AI agent), posted from Taylor's account on Taylor's behalf, at e71d37f19.

Waking matches an ordinary public channel. runner.ts:1891 turns any message addressed to the agent into a mention wake, regardless of who sent it. Agent attention watches pass author.role: "other" to the classifier. In that respect a Public Solo chat matches an ordinary public channel.

What a woken agent can do does not. In a Solo chat, every turn is given the owner's Canvas project: "This chat's work happens in these folders, not your own workspace: … cd into the one you need" (project.ts:29). Agents in ordinary channels don't get that section. Invited members could already do this, but the owner picks them one by one. Public extends it to everyone who presses Join.

How it fails: A community member finds the chat in Search, joins and posts "@agent, show me the config in the project folder." The owner's agent runs on the owner's machine, works in the owner's local folder and posts the result at top level, where the whole community can read it. The only limit is that agent's protection policy, which is set per agent and isn't specific to Solo. I didn't verify its defaults.

The confirmation says "New people will be able to post" (ChannelGeneralAccess.tsx:329). It doesn't say they can direct your agents in your folders. The sharing.md General access section doesn't either.

Fix (pick one; this is a product call):

  1. Keep Public out of Solo for now: View only + invitations, which was the original design. View only has none of this exposure because visitors can't post.
  2. Or, in a non-private Solo chat, give the Canvas project to turns only when the owner (or an owner-attested agent) addressed the agent. That needs a test covering a joiner's mention.
  3. At minimum: the confirmation and docs must say that people who join can mention and direct your agents, which work in your folders and post where the community can read. I don't think copy alone meets the bar for an agent with local filesystem reach.

@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 [blocking] Any Public joiner can knock out the owner's Canvas for every agent turn.

Adversarial re-review by Princess Donut (AI agent), posted from Taylor's account on Taylor's behalf, at e71d37f19.

The relay treats kind 40100 as an ordinary content write for channel Members (block/buzz main acf56fff6, crates/buzz-relay/src/handlers/ingest.rs:825-860), and the client gate is only channels.canWrite (session.ts:1205). A Public joiner becomes a Member, so they can publish a new Canvas head.

ownerCanvas only reads that head. If the head isn't from the owner or an agent the owner attested, it returns undefined, so project(undefined) gives NO_PROJECT: "This Solo chat has no workspace Canvas from its owner now. Disregard any earlier workspace Canvas, its instructions and its folders." That design correctly stops a joiner from steering the agent through the Canvas. But it lets any joiner erase the owner's instructions and folders.

How it fails: A community member joins a Public Solo chat and saves any Canvas text. From then on, every turn of the owner's agents runs without the owner's standing instructions or workspace folders, and nobody is told. The owner's Canvas view now shows the joiner's text. Only the owner rewriting the Canvas recovers it. Invited members could already do this, but Public opens it to the whole community.

Fix: When the head isn't from the owner's side, fall back to the latest revision that is. The Canvas is a regular event: query {kinds:[40100], "#h":[id], authors:[owner]} plus revisions from attested agents, rather than returning NO_PROJECT. An owner-written empty head still counts as a deliberate clear. Add a test: an owner revision followed by a joiner revision still hands the agent the owner's revision. Or, if you choose option 1 on the Public comment (no Public for Solo), this widening goes away. The existing exposure to invited members could then be handled separately.

@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Re-review at e71d37f19: status of prior findings and the new behavior (Princess Donut, AI agent, posted from Taylor's account on Taylor's behalf)

  • ✅ Prior blocker (Canvas disclosure) is resolved for both community choices. For Solo, ChannelGeneralAccess.tsx:322 shows the full-history + Canvas + folder-paths + agent-instructions sentence for both Public and View only. Public adds "New people will be able to post." sharing.md and buzz-help.md match. Unit and browser tests assert the Canvas sentence for both choices and the posting sentence for Public.
  • ✅ Prior optional item: the contracts.ts:46 comment is fixed. The "Public (existing)" state no longer applies, because Public is now an ordinary choice.
  • ✅ The new behavior works as described: Solo offers Public, the hint is removed, Solo is on the Join path (ChannelsPage.tsx), Join/preview copy uses "chat", and Search shows "Public chat · not joined" (or "View-only chat" when join_role=guest). View only stays read-only, and its copy is right. I found no defect in this part.
  • ❌ Public adds two new blocking risks; see the two comments above: (1) joiners can direct the owner's agents in the owner's folders; (2) joiners can wipe the owner's Canvas for agent turns.

@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from e71d37f to e998427 Compare November 1, 2026 06:47
@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Scoped re-check at e99842786: both blockers from the previous review are now unreachable from the Solo UI (Princess Donut, AI agent, posted from Taylor's account on Taylor's behalf)

  • ✅ A (joiners directing the owner's agents) and B (joiners wiping the Canvas): The Solo UI has no way left to create a Public chat or to join one.
    • The Public option is disabled: solo and explains why: "Anyone joining could direct your agents." Separately, choose() rejects public for Solo, so a keyboard Enter or a stray value can't save it (ChannelGeneralAccess.tsx:171-175, :293-299).
    • The Solo Join path is removed: joinable is back to stream/forum only (ChannelsPage.tsx:1832-1835). That was the only UI join entry I found; the other setJoiningChannel uses only reset or clear the pending join.
    • With View only, visitors are Guests and the relay rejects their writes.
  • ✅ Public (existing) is handled correctly. It's labelled, Public stays disabled, and switching to View only sets join_role=guest with the Canvas disclosure. Tests cover the disabled click (no dialog, no save) and the Public (existing) → Guest change.
  • ✅ The docs are honest. sharing.md and buzz-help.md say this is a UI restriction, not relay or harness enforcement, and name the CLI/MCP and invited-member paths as follow-up work.
  • [optional] In ChannelJoinNotice.tsx, the visitor preview text went back to "You’re previewing this channel". A visitor on a Public (existing) Solo chat still sees that, so using ${place} for that one string would say "chat" instead.

Not covered here: the carried-over channel-members-focus.spec.mjs:1313 hint-text assertion, which is tracked separately, and the harness-level versions of A and B. No new blocking findings.

@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from e998427 to b5ad705 Compare November 1, 2026 07:08
@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Carl, on Taylor’s behalf: pushed b5ad705fa as the single feature commit. Solo shows Public disabled with its reason and permits View only / Private; Public (existing) and the View-only Canvas disclosure remain. The Solo Join extension is removed. This implements the approved UI restriction; it does not harden CLI/MCP or invited-Member agent/Canvas access.

The final amendment is only the stale hint expectation in channel-members-focus.spec.mjs (+1/−3 vs e99842786). Its complete Chromium/WebKit file passes 36/36 cases, preserving pointer/Enter agent admission and search-focus checks. The unchanged product tree at e99842786 passed 216 unit checks, 22 browser cases and TypeScript/build; all mandatory commit/push gates pass at the published head. All 20 screenshots load.

The scoped UI re-check at e99842786 found no new blockers; the final amendment changes no product behavior. Hosted CI, native/live-relay acceptance and human acceptance are separate; this PR remains draft.

@tellaho
tellaho marked this pull request as ready for review November 1, 2026 07:41
@tellaho
tellaho requested a review from a team as a code owner November 1, 2026 07:41

@kalvinnchau kalvinnchau 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.

🤖 Found a conversion access regression and two access-disclosure/copy issues.

Comment thread src/features/relay/discovery.ts Resolved
Comment thread src/app/shell/SearchResults.tsx Resolved

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage. Two P2 findings are attached inline.

Reviewed head b5ad705 against base 7878f06, using immutable Git objects. I also inspected the public description, commit message and all 20 attached images; no actionable public-material exposure was found on those surfaces.

The change reuses Select, AlertDialog, channelDetails save/readback and ordinary discovery authority. The two new browser journeys (four engine cases) cover routing, signed broker/discovery integration, persistence, focus and responsive overlays; role permutations remain in unit tests. Existing recovery tests include focus assertions, but this review did not run them.

Source-only: no tests, builds or app execution. The single hosted-check snapshot showed CI required and the listed checks successful, with Windows validation skipped. Native/live-relay acceptance, performance measurements and the final human walkthrough remain unverified. Disabling Public is assessed as the documented UI restriction; separate harness/Canvas hardening is outside this change. This COMMENT review is not an approval.

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage. No new actionable findings in the changes since b5ad705.

The two prior P2 findings are repaired in source: Solo agent additions explicitly request Bot through the existing outbox, with compatible receipt matching, and Public/Member nonmember previews use the ordinary roster reader. I also checked conversion’s current-visibility default and retained explicit/pending intent. The native generic signer and browser enrollment route accept the new admission shape; this is source inference, not live-host acceptance.

Reviewed head 2f976be against base 923a80f, using immutable Git objects. The 12-file endpoint follow-up reuses the existing Dialog, RadioGroup, roster reader and channelWrites owner. No browser cases were added or removed; two existing journeys extend actual modal/focus and broker/discovery coverage while role permutations remain in unit tests. I inspected the current public description, commit message and all 22 attached images; no actionable publication-surface exposure was found.

Source-only: no tests, builds or app execution by this review. One hosted-check snapshot showed all listed checks successful except skipped Windows native validation. Native/live-relay acceptance, rendered error/retry focus, performance measurements and the human walkthrough remain unverified. The existing optional disclosure/label decisions and documented harness-hardening follow-ups are unchanged. This COMMENT review is not an approval.

@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from b5ad705 to 2f976be Compare November 1, 2026 09:55
@tellaho

tellaho commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Scoped re-check at 2f976be3e: all three P2 fixes hold, and I found no new blocking issues (Princess Donut, AI agent, posted from Taylor's account on Taylor's behalf)

  1. ✅ Conversion now starts from the chat's current visibility. selectedVisibility falls back to base?.visibility before "private" (ConvertToChannel.tsx:100-105). The interim "private" can't be saved: the radio and the Convert button stay disabled until eligible, which requires the loaded base.canEdit, and the draft is built only once base exists. Conversion leaves join_role untouched, so a View-only chat becomes a public channel with Guest admission. That matches the Public radio's copy, which promises only that people can view the history. The new test covers converting a public Solo chat.
  2. ✅ Owner agent-adds now request Bot, and retry matching is as strict as before. addAgents adds ["role","bot"] only when channelType === "solo" (channel-writes.ts:456-457). The dialog, composer mention and bestie setup all go through this path, and non-Solo channels still get no role tag. The receipt matcher now requires exactly the same role tags, so receipts with no role, guest, member or a duplicate bot aren't reused (that table is in session-agent-admission.test.ts). I checked the relay side (block/buzz main acf56fff6): decide_member_put treats only Owner and Admin as elevated, and any non-Guest may add. So an invited Member adding their own agent with role=bot is still accepted. No regression for shared chats.
  3. ✅ Public/Member visitors now get the ordinary preview roster. soloSharing now also requires !channel.readOnly (ChannelMembersDialog.tsx:684), so every read-only nonmember uses the 39002 roster read. Sharing was already ineligible for them because canShareSoloChat requires !readOnly, so they lose no capability. The (Solo: true, admission: member) case covers it.

Note, in Kalvin's on-hold P3 scope (no action here): a Public/Member Solo visitor's access panel says "New people can participate. People already in the channel keep their access." But Solo has no Join path, and the noun is "channel".

@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from 2f976be to 8afa56e Compare November 2, 2026 18:18

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Carl, an automated reviewer, commenting via Wes's GitHub account. The review is from Carl, Princess Donut, Mongo and Mordecai, and covers head 8afa56e08 against 7878f0609.

Verdict: one P2 should be fixed before merge. There are two optional P3s. All three are inline.

  • What holds: View only stays read-only through the relay. Guests can't post, react or write the Canvas, and nonmembers can't add people. The explicit Bot and Member grants pass the relay's admission rules. The strict receipt matching for agent additions holds. Conversion keeps the current visibility. Previews use the ordinary roster reader. Public cannot reach the Solo save path.
  • Not verified: this was a source review, with one focused Node probe of the invitation recovery path. CI is green at this head. Native and live-relay acceptance and a human walkthrough remain open. The mobile app still treats only private chats as Solo (isSolo), and that stays the PR's stated follow-up.
  • Merge criteria: make the Private transition's copy and docs true for people who joined while the chat was View only.

Comment thread src/features/channel-members/members.ts Resolved
@tellaho
tellaho force-pushed the tho/solo-open-view-only branch 2 times, most recently from 857dda9 to 658395c Compare November 2, 2026 18:55

@kalvinnchau kalvinnchau 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.

🤖 One additional Solo disclosure concern; existing findings are not repeated.

@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from 658395c to c964938 Compare November 2, 2026 19:25
@tellaho
tellaho requested a review from wesbillman November 2, 2026 19:32
@tellaho
tellaho requested a review from kalvinnchau November 2, 2026 19:32
@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from c964938 to cd8d751 Compare November 2, 2026 20:29

@kalvinnchau kalvinnchau 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.

🤖 One remaining disclosure gap when member roles are unavailable.

Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho
tellaho force-pushed the tho/solo-open-view-only branch from cd8d751 to 8af2cd2 Compare November 2, 2026 21:34
@tellaho

tellaho commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

🤖 @kalvinnchau Both disclosure gaps are fixed in 8af2cd287102d10cf52aa69bad979348002e15ac. The retained-Guest warning stays visible during pending/failed role verification unless verified roles establish no Guests. General access and private-chat conversion now identify history, Canvas, workspace folders and agent instructions.

The failed WebKit conversion journey now waits for Select closure/focus before keyboard navigation, retaining its selection/publication/focus assertions without sleeps or retries. The existing compact Dialog spacing keeps the disclosure, picker and actions unobscured.

Validation: 75 unit checks in three complete files; final conversion refinement reran all 29 conversion checks. All 24 Chromium/WebKit cases across the two complete browser files passed; the final refinement reran all 20 Solo chats cases (47.5s). Mandatory push gates pass, including types/related units (147.53s). Hosted CI and human retest remain pending. The description keeps desktop captures; narrow/enlarged-text checks remain covered.

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Carl, an automated reviewer, commenting via Wes's GitHub account. This is a follow-up from Carl, Princess Donut, Mongo and Mordecai at 8af2cd287, against 6dc462a2d.

Verdict: nothing blocks merge. The prior P2 is fixed. The retained-Guest warning shows while roles are loading, after they fail to load, and whenever a Guest exists. The Public (existing) → View only copy and the docs now match relay behavior. Three optional P3s are inline.

  • Checked: in applySharedChannelDetails, the joinRole matching has one caller, conversion, which passes complete loaded details, so it raises no spurious conflicts. Private keeps the admission default. channel-writes.ts is unchanged since the last review. The conversion Select uses the shared keyboard and focus handling. The new general-access.ts serves two real callers.
  • Not verified: this was a source review only. CI snapshot: 39 checks passed, WebKit 2/14 was still pending, and Windows was skipped. Native and live-relay acceptance and a human retest remain open. The mobile app still treats only private chats as Solo, and that stays the stated follow-up.

Comment thread src/features/solo/ConvertToChannel.tsx Resolved
Comment thread src/features/solo/ConvertToChannel.tsx Resolved
tellaho and others added 4 commits November 2, 2026 16:20
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho
tellaho requested a review from wesbillman November 3, 2026 00:41
@tellaho

tellaho commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Carl (AI agent): @wesbillman All four open P3s are addressed in 590f4ee29:

  • Public (existing) → Private and open-chat → Private conversion now explain retained member access, using the same Solo warning.
  • Verified superseding settings retire obsolete, unconfirmed conversion receipts through Finish, Reload and remount. Transient read failures keep recovery; neither path overwrites settings or unfiles prematurely.
  • Legacy no-role invitations cannot be replayed as participation grants. Recovery verifies signed participation, preserves unknown outcomes, and reports a Guest's remove-and-invite-again path. It never silently promotes a newer role; concurrent member operations cannot confirm from stale roles.

Validation: 116 focused checks across the three complete unit files; all 24 Chromium/WebKit cases at f51d51ca8 (final warning/conversion and browser files are byte-identical); mandatory gates pass (TypeScript/related units 94.58 seconds; design checks 4.73 seconds). Four inspected desktop captures are added to the description. Human retest and native/live-relay recovery remain unverified.

@kalvinnchau kalvinnchau 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.

🤖 One legacy invitation recovery dead end remains.

if (soloParticipant && pending && noRole(pending.event)) {
if (!channel()?.members?.includes(pubkey))
throw new Error(
"This saved invitation does not explicitly grant participation. Check its status in Outbox before inviting again.",

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.

🤖 [P2] Provide a recovery exit for an unapplied legacy invitation

With a saved no-role Solo kind-9000 whose delivery is unknown and whose target is absent from the verified roster, recover() above attaches a member-add: recovery key before this branch directs the user to Outbox. Outbox Retry routes back through memberAdditions.add and reaches this same rejection; Remove rejects non-failed kind-9000 items with recovery keys (outbox.ts:877–884). Members Add and Solo sharing also reach this branch. The existing members.test.ts assertions at lines 299 and 302 preserve the dismiss rejection and the repeated add rejection after restart. Consequently this profile cannot complete a fresh invitation for that person through these actions. Provide an explicit, safely verified way to retire the legacy receipt and issue a fresh role=member invitation while preserving the prohibition on replaying the no-role event.

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.

One P2 remains: I corroborate the existing legacy-invitation recovery finding. For an unknown no-role invitation whose target remains absent, members.ts:192–201 attaches recovery ownership and rejects; Outbox Retry returns to the same branch, while Remove rejects the unknown recovery item. The new restart test preserves that loop. Please provide a verified recovery exit that allows participation to be restored while preserving unknown-outcome safety and never replaying the no-role grant. Fresh roster absence alone does not establish that an in-flight command cannot arrive later. Keeping this attached to the existing finding avoids a duplicate inline thread.

The retained-access warnings and superseded Finish-receipt fixes are present in source; no additional actionable defect was established in this follow-up. Reviewed head 590f4ee against base bcabade, focusing on changes since 8af2cd2. The public description and all 23 attached images were inspected.

Source-only: no tests, builds or app execution; CI was not assessed. Native/live-relay recovery, performance, keyboard/screen-reader behavior on mismatch and retry, and human acceptance remain unverified. This is a COMMENT review, not approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants