Skip to content

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

Draft
tellaho wants to merge 7 commits into
tho/sidebar-grouping-starfrom
tho/sidebar-lifecycle
Draft

tellaho wants to merge 7 commits into
tho/sidebar-grouping-starfrom
tho/sidebar-lifecycle

Conversation

@tellaho

@tellaho tellaho commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

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 the same-repository tho/sidebar-grouping-star mirror of grouping/Star #10, not main; menu #89 → grouping/Star #10 → this lifecycle sibling. #90 was superseded by #10. Exact base: 0cbb69ab8e33b5ac11b1e97d503b45f660b1fd09. Sorting and mute/read remain independent siblings. Reconciliation preserves the unified Move to… chooser, Starred-to-group moves, removal to Channels, and the preference owner's ordered two-record write queue and refresh/cancellation fencing.

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.
  • Membership accepts the relay's normal four-field p tags (plus two-/three-field forms), without granting authority from membership hints. Administrator authority stays in 39001; invalid keys and duplicate members still fail closed. Failed menu permission reads show Channel actions unavailable with Retry instead of raw parser errors. Confirmation wording is unchanged.
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. Pending reads are silent; unavailable and retry states remain explicit.

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 have no trailing ellipsis; Leave and the ownership-transfer explanation are absent for the last owner.
  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

Latest head: e34cbbe6087281260113f9dd6d1975e29801e4b1. Removed the “Checking channel permissions…” menu row. Fresh permission reads still run; actions appear only when verified, and failed reads retain Retry. Mounted tests gate both initial and retry lookups to verify silence without unchecked actions. Full Vitest passed 152 files / 1,644 tests (16.50s) on the working update atop 2b59d01; pre-commit preserved that tree. Clean-head push hooks passed TypeScript and 65 files / 789 tests. Changed-file Biome and diff checks passed. Browser/build/native checks were not repeated for this presentation-only change; ready-state screenshots below are unchanged.

Previous menu-label update: 2b59d0139991c6e53d9269e98cffcb05ea3e7711. Archive/Delete/Leave/Hide labels no longer end in ellipses. Forbidden Leave and its ownership-transfer explanation are omitted; permitted Leave, confirmation dialogs and domain authorization remain intact. “Move to…” is unchanged.

Validation for this update: full Vitest 152 files / 1,644 tests (16.26s wall time), production build/TypeScript, changed-file Biome, and all 6 lifecycle browser cases across Chromium/WebKit (31.2s, zero retries) passed on the working update atop a455718. The committed tree was verified unchanged by pre-commit; clean-head pre-push TypeScript plus 65 files / 789 tests passed. Two mounted cases added, no browser cases added/removed. No live destructive writes. Current-head hosted results, broader browser/native checks and acceptance are not claimed.

The implementation still incorporates grouping snapshot 0cbb69a; its mirror has since advanced to 67146d8. This menu-only update does not rebase or validate that newer dependency.

Previous roster/grouping validation snapshot: a4557189674d9572ff6d6cb3134fc98c51136d4e, base 0cbb69ab8e33b5ac11b1e97d503b45f660b1fd09. The following full-suite/build/browser checks ran on the six-file working update atop rebased 040898c220bc754eb10f333cbaf6a60c2730123d; the committed tree was verified identical after the pre-commit hook (33daf0d49ba63d258868522780773f10264066ab). Pre-push ran on the clean committed head.

  • bin/pnpm check: passed Biome (657 files), TypeScript, design TypeScript and type/color/contrast/foundation checks.
  • bin/pnpm exec vitest run: 152 files / 1,642 tests passed, 16.26s wall time. Includes broker-route integration, mounted UI and new roster regressions.
  • bin/pnpm build: passed (existing >500 kB chunk warning remains).
  • Above Chromium/WebKit command: 12/12 passed, 1.5m total, two workers, zero retries. Includes Archive, Hide, typed Delete/access purge/reload, and grouping/Star integration with corrected signed roster fixtures.
  • Normal pre-commit hook passed without changing the tree. Normal pre-push TypeScript and selected Vitest checks passed: 65 files / 787 tests, 16.23s Vitest wall time, 82.31s summed test time. git diff --check passed against the exact base. Both outgoing lifecycle commits retain verified authorship and DCO trailers.
  • New-head hosted snapshot (2026-09-22): DCO Check passed for a455718; CI run 35763295099 is running/queued on that exact head. Earlier all-green checks belong to db4efaf and do not validate this rebased/fixed head. Remote branch and refs/pull/92/head both resolve to a4557189674d9572ff6d6cb3134fc98c51136d4e.

Older evidence, not rerun for this head: dm-labels.spec.mjs passed 4/4 in both engines; serial cold/warm opening measurements passed 2/2 (24.8s total, cold visible upper bounds 339/345ms including Playwright roundtrip, warm paint 23–34ms Chromium / 38–70ms WebKit). These were local Apple Silicon macOS fixture measurements on the original lifecycle snapshot, not current-head or hosted-network guarantees.

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 roster regression first failed the full Vitest run (28 tests across two files plus unhandled errors) after replacing the incorrect two-field fixtures with the actual relay shape. Accepting the optional membership fields restored the full 1,642-test pass while new tests retain rejection before signing/publication for malformed and duplicate members, and prove hints cannot grant admin authority. Relay contract source: block/buzz 70895b355fcea9f99894b426c020052b715bd368, crates/buzz-relay/src/handlers/side_effects.rs:1050–1057; legacy reader: desktop/src-tauri/src/nostr_convert.rs:243–265. No new browser cases were needed for this correction. Earlier implementation 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

Latest lifecycle menu capture; confirmation and grouping captures are unchanged from the preceding update. Dark mode with signed fixture data; narrow crops, no live community content. Real product UI, not mockups or a component showcase.

Updated lifecycle menu without action ellipses or forbidden Leave

Archive confirmation using the shared design tokens

The existing non-UUID grouping fixture intentionally cannot resolve lifecycle permissions; it demonstrates safe unavailable/Retry copy alongside the new group chooser.

Saved-group menu with Starred, Work and safe lifecycle retry

Carl added 3 commits September 17, 2026 07:46
Add Base UI menu and context-menu primitives, shared floating styles, and the component registry/specimen entry.

Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Expose group assignment and Star/Unstar through confirmed host-owned preference writes. Preserve unrelated encrypted data, fence stale reads and cancelled sessions, and restore keyboard focus after exclusive row relocation.

Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Offer Starred and saved groups in one destination chooser. Confirm the destination assignment before clearing Star so removal returns to Channels without restoring a hidden group after reload. Retain confirmed local placement through partial failures, fence overlapping refreshes, and cover ordered writes, cancellation, retry and browser focus.

Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Carl added 2 commits September 17, 2026 11:36
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>
Accept the relay's four-field membership roster without using hints as administrator authority. Exercise valid and malformed roster shapes, correct browser fixtures, and offer safe retry copy instead of raw protocol errors.

Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
@tellaho
tellaho force-pushed the tho/sidebar-lifecycle branch from db4efaf to a455718 Compare September 22, 2026 17:51
@tellaho
tellaho force-pushed the tho/sidebar-grouping-star branch from 0cbb69a to 67146d8 Compare September 22, 2026 22:02
Carl added 2 commits September 22, 2026 17:02
Remove action ellipses and omit forbidden Leave actions instead of displaying a disabled item and ownership guidance. Preserve confirmation and domain authorization, with mounted and browser regressions.

Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Wait silently for fresh menu permissions while retaining failed-read retry and all authorization checks. Cover pending initial and retry lookups without exposing unchecked actions.

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