Skip to content

feat(channels): confirm archive delete leave and dm hide actions - #92

Draft
tellaho wants to merge 1 commit into
tho/sidebar-grouping-starfrom
tho/sidebar-lifecycle
Draft

tellaho wants to merge 1 commit into
tho/sidebar-grouping-starfrom
tho/sidebar-lifecycle

Conversation

@tellaho

@tellaho tellaho commented Sep 17, 2026

Copy link
Copy Markdown

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), not main; 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

  • Archive: direct owner/admin; Delete: direct owner only; Leave: cannot remove the last owner. DMs offer Hide only.
  • Fresh exact relay-authored 39000/39001/39002 checks before signing and again before publication. The relay remains the final authorization boundary; these reads are not an atomic permission lease.
  • Dedicated 9002/9008/9022/41012 commands stay outside the durable message outbox. No automatic destructive replay.
  • Archive preserves membership. Confirmed Delete/Leave reuse access-loss purging. 30622 DM visibility only filters sidebar rows; exact conversation navigation remains available.
  • Uncertain delivery warns that the request may have taken effect and requires closing/refreshing before another attempt; a definitive rejection remains explicitly retryable.
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

  1. Use the fixture-backed browser journey below; do not test destructive actions against a real community just to review this PR.
  2. Open Messages, focus Lifecycle channel, and use Shift+F10 or right-click. Archive/Delete are available; Leave explains the last-owner restriction.
  3. Open Archive, cancel with Escape, and verify focus returns to that row. Confirm Archive and verify the row disappears while navigation moves to Alpha.
  4. In the separate Delete journey, enter the exact channel name to enable confirmation. Confirm and reload: the removed channel stays absent.
  5. Hide the fixture DM and reload. Its row stays hidden, but exact conversation navigation still opens its composer; only kind 41012 was published.
bin/pnpm build
bin/pnpm exec playwright test --config tests/browser/playwright.config.mjs \
  tests/browser/channel-lifecycle.spec.mjs tests/browser/navigation-groups.spec.mjs \
  --project chromium --project webkit --no-deps

Validation and remaining gates

Implementation snapshot: db4efafbfb3ae92d84259a1b59148f4a83744ddb, base 0e7bf1ffe0cb65c39d657d5ae799a83c75bb7cfb. 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).
  • Above Chromium/WebKit command: 12/12 passed, 1.1m total; 3 new lifecycle cases per engine plus existing grouping/Star cases. New lifecycle cases each took roughly 9–10s including fixture setup.
  • 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.
  • Required pre-commit and pre-push hooks passed; pre-push selected 64 files / 772 tests plus TypeScript. git diff --check passed.

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.now retained 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.

Channel lifecycle menu with last-owner protection

Archive confirmation using the shared design tokens

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

1 participant