Conversation
Resolve fresh relay-owned channel permissions, keep lifecycle commands outside the message outbox, and confirm authoritative state before removing rows. Preserve DM membership, fence cancelled sessions, and make ambiguous publication recoverable without blind resubmission. Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Category: new-feature
User Impact: Users can archive, delete or leave supported channels, and hide DMs without removing anyone from the conversation.
Problem: The new sidebar lacked channel lifecycle actions, leaving users dependent on another client to manage their conversations.
Solution: Add type-appropriate actions backed by fresh channel permissions and explicit confirmation. Remove rows only after relay-owned state confirms the change, retaining recovery when publication is rejected or uncertain.
Stack: targets
tho/sidebar-grouping-star(#90), notmain; menu #89 → grouping/Star #90 → this lifecycle sibling. Sorting and mute/read are independent siblings. This PR does not alter or retire #10.Source: buzz://message?channel=b9ab2a04-14c4-440d-8c82-aebfbc1caa68&id=2fafae420919217d2b096b02c22f0a045356b19708c16a52d1e65d54d1741d41
Changes
39000/39001/39002checks before signing and again before publication. The relay remains the final authorization boundary; these reads are not an atomic permission lease.9002/9008/9022/41012commands stay outside the durable message outbox. No automatic destructive replay.30622DM visibility only filters sidebar rows; exact conversation navigation remains available.File changes
src/features/relay/channel-lifecycle-protocol.ts
Defines the narrow lifecycle wire shapes and projects direct role/type/membership capabilities from relay-authored channel records.
src/features/relay/channel-lifecycle.ts
Owns fresh permission checks, signing and publication confirmation, cancellation, uncertain outcomes and per-viewer DM visibility.
src/features/relay/channel-lifecycle.test.ts
Covers all four commands, malformed/foreign authority, role revocation, signer mutation, failed/uncertain delivery, session retirement and visibility replacement.
src/features/relay/transport.ts
Exposes an optional dedicated broker lifecycle capability without expanding message-outbox kinds.
src/features/relay/session.ts
Adds only 15 lines of explicitly authorized composition, cancellation, clear and disposal wiring; domain state stays outside FOUNDATION.
dev/relay-broker.mjs
Adds shape-limited lifecycle sign/publish routes through the existing authenticated host boundary.
dev/relay-broker-api.test.mjs
Exercises the real host routes and rejects general metadata changes, other-member removal, malformed commands, foreign signers and message-writer misuse.
src/bundled/channels/ChannelLifecycleMenu.tsx
Loads authority only while a row menu is mounted, with explicit loading, unavailable and retry states.
src/bundled/channels/ChannelLifecycleDialog.tsx
Confirms every action, requires the channel name for Delete and keeps failures recoverable without blind resubmission after uncertainty.
src/bundled/channels/ChannelLifecycleDialog.module.css
Uses shared type, spacing, surface and color tokens for the responsive native modal.
src/bundled/channels/ChannelLifecycleMenu.test.tsx
Mounts the real menu/dialog to cover permissions, confirmations, lockout, recovery and stale completions after unmount.
src/bundled/channels/ChannelsPage.tsx
Integrates row actions, separate DM visibility, post-removal navigation and focus restoration; hidden DMs remain accessible by exact navigation.
tests/browser/channel-lifecycle.spec.mjs
Adds representative built-app Archive, Hide and Delete journeys across the real broker and native modal.
tests/browser/fixture.mjs
Adds opt-in signed lifecycle fixtures and modeled relay effects, without live destructive writes.
tests/browser/policy-relay.mjs
Recognizes the exact three-coordinate capability query in the production-broker fixture.
tests/browser/navigation-groups.spec.mjs
Preserves keyboard/group/star assertions with lifecycle items now following the existing menu items.
docs/channels.md
Documents authority boundaries, publication ambiguity, sidebar-only hiding and intentionally deferred parity.
Reproduction steps
41012was published.Validation and remaining gates
Implementation snapshot:
db4efafbfb3ae92d84259a1b59148f4a83744ddb, base0e7bf1ffe0cb65c39d657d5ae799a83c75bb7cfb. Checks ran against the pre-commit working tree; the staged/committed tree was verified unchanged by hooks. The final adjustment only controls the unit test's timestamp, not product code.bin/pnpm check: full Biome, TypeScript, design TypeScript and design token/contrast/foundation checks passed.bin/pnpm exec vitest run: 151 files / 1,627 tests passed, 16.28s wall time, 94.77s summed test time. Includes actual broker route integration and mounted UI regressions.bin/pnpm build: passed (existing large-chunk warning remains).dm-labels.spec.mjs: 4/4 passed, both engines, during the earlier expanded run; this verifies profile recovery through access purge. Subsequent product edit only corrected the menu-to-modal callback guard.channel-opening.spec.mjs, Chromium/WebKit measurement projects, serial--workers=1 --no-deps: 2/2 passed, 24.8s. Cold visible upper bounds 339/345ms include the Playwright roundtrip; warm browser paint times 23–34ms Chromium / 38–70ms WebKit, without head reads. Local Apple Silicon macOS fixture measurements, not hosted CI/network guarantees.git diff --checkpassed.Browser coverage rationale: +3 scenarios per engine, no cases removed. Archive proves keyboard/menu/native-modal handoff and focus; Hide proves actual routing and reload visibility without access deletion; Delete proves the separate access-purge/navigation path. Role and failure permutations stay in domain/React tests; Leave uses those lower layers rather than another equivalent full-app journey. Existing grouping keyboard assertions now account for the final lifecycle retry item, without dropping End/Home traversal.
Fail-then-pass evidence: the browser cases rejected an over-strict menu generation guard that prevented native dialogs from mounting; removing that invalid guard restored all six lifecycle executions. Pre-push also caught the unit command-equality assertion crossing a wall-clock second; controlling
Date.nowretained the exact timestamp assertion and the hook passed. No retries, sleeps, broadened error allowlists or weakened assertions were used to hide those failures.Deferred: full
just scan/all-browser suite, Node CLI/Rust/native validation, independent review and attended native UI acceptance. This remains a draft, not integration-ready. No live destructive relay writes were performed. There is no matched before/after fixture-performance baseline or hosted CI performance claim.Explicit scope limits: development-broker lifecycle writes only (native/direct-signer capability deferred); direct channel roles only (delegated owner-agent/community-admin authority deferred); no in-app unarchive or DM reopen/unhide; DM visibility refreshes with roster refresh, not a new live cross-device subscription. Cancellation cannot retract an already-sent request. Publication confirmation is bounded and may require an explicit refresh.
Screenshots / demos
Real built product UI in dark mode using signed fixture data; narrow crops, no live community content. These are the new action menu and its confirmation state, not mockups or a component showcase.