feat(sidebar): move channels into saved groups and starred - #207
Conversation
5efc4af to
0b4e994
Compare
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
Changes requested: one P2 request-decoding defect, detailed inline. Merge criterion: preserve valid Unicode section names across arbitrary HTTP body chunks and add a broker-boundary regression that verifies the persisted and returned name.
Source-only whole-feature review of 7e0a05e2e6bef9d2f49aeb5f9e9b5aec3c7846a3 against 30c3589ba57ce926ac19c5526d8b87cde35d6e48. Existing CI run 36095172384 passed on this head’s integration merge with main df7b7e7f: 3,789 unit tests and 674 Chromium/WebKit journeys, without hosted retries. No reviewer execution or CI rerun. Native/package, live-relay and real restart persistence remain unverified.
The AgentModelPicker selector change retains the pending-state and exact-selection assertions; it is not a blocker. Placeholder/no-shortcode icon presentation follows the originating product discussion, not an unintended fallback regression. Documented cross-record partial saves and whole-record cross-device races remain accepted limitations.
|
Fixed the UTF-8 group-name corruption in The assignment/Star collector now enforces the existing 2,048-byte limit on incoming bytes, joins them, and decodes once. The broker regression forces one-byte chunks and checks Validation: the new regression failed against — Carl, AI agent, posting through Taylor’s account under standing authorization. |
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
Source review clear; required CI remains red. The prior Unicode blocker is fixed at 752aaaccc878eadfdbc92d741342cde41e70a622. The shared sidebar assignment/star collector now bounds raw bytes and decodes once. Its new HTTP-boundary regressions force one-byte chunks, verify the exact returned and encrypted/persisted name, and preserve both routes’ byte-limit rejection. No new source blocker or weakened existing assertion found in the two-file change since 7e0a05e2 (base 30c3589ba57ce926ac19c5526d8b87cde35d6e48).
Exact-head CI: all three new regressions and 674 browser journeys passed; JavaScript finished 3,791/3,792 because broker.integration.test.ts:128 failed its first-versus-second publish timing comparison. That test uses the unchanged /publish collector, not this sidebar collector. Its failure cause remains unestablished; this is not a CI-green or merge-ready verdict. Required checks still need to pass.
Source-only review, no execution or reruns. Native/package, live-relay and real-restart persistence remain unverified. This is a follow-up comment, not approval.
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
Focused re-review clear at d47225108363093987d31d0cc5ed5c9966d9f328; existing required CI is now green. No actionable blocker found in the one-test change since 752aaaccc878eadfdbc92d741342cde41e70a622.
- The replacement assertions retain the real broker publication gate, pending outbox, successful delivery of both messages, attachment roundtrip and timing-span continuity. Removing the independent first-versus-second wall-clock comparison does not remove controlled-clock latency coverage. Production code is unchanged in this delta; the previously verified Unicode repair remains intact.
- CI run 36155055909, attempt 1, passed the repaired broker test, all 3,884 JavaScript tests, 710 Chromium/WebKit journeys, seven measurements and Rust checks. Its tested merge is
b3e129815e8e818ff55c4fb9b4276ecd2b70a03f, combining this head with mainbebcd66e; merge tree37cba00a0c0a2f0449dbfd8345972845cd56ecd5differs from the head tree. This is integration CI evidence, not a head-only run. - Remaining gates: GitHub still reports
CHANGES_REQUESTED/blocked; this comment is not approval or dismissal of the historical review. Newer main0d703a60has not been CI-tested with this head. Windows was skipped; native, live-relay and real-restart persistence remain unvalidated here. Source-only review plus existing hosted evidence, with no execution or CI rerun. I contributed earlier fixes, so this is not independent/code-owner approval.
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
Re-review complete at d47225108363093987d31d0cc5ed5c9966d9f328 against 30c3589ba57ce926ac19c5526d8b87cde35d6e48: no blocking defects found. Two non-blocking P3 recovery issues are recorded inline.
- The earlier Unicode-corruption blocker is fixed. Broker tests now split multibyte names into single-byte chunks and check both the response and decrypted publication. The timing-test repair preserves the real publication gate and delivery assertions; controlled-clock attribution remains covered separately. The UI, shared persistence and broker review lanes have all returned and been integrated.
- Validation: source-only review; no local builds/tests or CI rerun. CI run 36155055909, attempt 1, is green: 3,884 JavaScript tests, 710 Chromium/WebKit journeys, seven measurements and Rust/tool checks. CI tested merge
b3e129815e8e818ff55c4fb9b4276ecd2b70a03fwith mainbebcd66e, not the head alone or the currently observed main03366998. Windows was skipped; packaged-native, live-relay and actual restart persistence remain unverified here. - GitHub still reports the historical
CHANGES_REQUESTEDverdict. This comment neither approves nor dismisses it; independent/code-owner review remains a merge gate. I contributed earlier fixes. The stale PR description should also be refreshed, but that is not a code blocker.
|
@wesbillman Both non-blocking recovery requests are fixed and individually answered/resolved at d75d0d3. The earlier Unicode blocker also has a direct resolution reply; no source blocker from those requests remains. Description refresh: replaced the stale opening status, added current validation and per-request commits, collapsed historical validation, marked the old Unicode blocker as fixed, and preserved all 27 screenshots with their actual d472251 provenance. The images are not mislabeled as captures of the new recovery behavior. Validation: current-head mandatory hooks pass (3,714 tests / 340 files, TypeScript, design types/guards); hosted DCO passes. All 18 grouping Chromium/WebKit journeys pass on the unchanged production/browser tree subsequently committed in 3cce884. New notice and keyboard regressions have fail-before/pass-after evidence. Merge conflict / CI: still blocked, not complete. Main 80511fa overlaps the broker timing-test repair, adding a 200 ms hold while retaining — Carl, AI agent, posting through Taylor’s account at Taylor’s request. |
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
No blocking code findings in this focused follow-up. Retry now explains rejected attempts; the previously reported collapsed-group and missing-row Dismiss cases are addressed. One narrow, non-blocking P3 focus edge remains inline.
Reviewed head d75d0d31148be22053003500e61ce98128eb389a against base/merge-base 30c3589ba57ce926ac19c5526d8b87cde35d6e48, concentrating on d4722510..d75d0d31. I contributed earlier fixes; an independent reviewer checked dismissal and the shared focus effect, and I verified the finding and retry/store wiring. Accepted persistence limitations were not reopened.
Validation: source review only; neither reviewer ran tests. The PR reports 3,714 passing Vitest tests at the pushed head and 18/18 Chromium/WebKit grouping journeys at a7e0ba04 plus the unchanged edits committed as 3cce8844; d75d0d31 changes only fixture typing. Those are author-reported results, not new review runs.
Separate merge-readiness gaps: GitHub still reports CONFLICTING / DIRTY. Exact-head checks are Semgrep, zizmor and DCO only, with no hosted Actions run or current-head integration pass. Resolve the main integration conflict and validate the resulting head before treating it as merge-ready. This COMMENT is not approval and does not clear the historical changes-requested review.
| sidebar.list.current?.querySelector<HTMLButtonElement>( | ||
| `[data-channel-id="${CSS.escape(rowFocus)}"]`, | ||
| ) | ||
| ?.focus({ preventScroll: true }); | ||
| ) ?? |
There was a problem hiding this comment.
[P3, non-blocking] Let a hidden target use the visible-row fallback
This first lookup accepts an existing but hidden row, bypassing the visible-row fallback. A source-confirmed path remains after the Dismiss fix:
- Retain a failed move for an unstarred channel assigned to group A.
- Refresh the independent personal catalog with that channel assigned to B, while the full preferences read fails. The catalog subscription requests a full refresh (
session.ts:1034–1041), but a legacy-read failure retains the old store data. The sidebar deliberately renders the personal projection when unwritable (ChannelSidebar.tsx:418–425). - Collapse B, then keyboard-activate the move notice’s Dismiss button.
focusChannelPlacement reads the stale store and expands A, not displayed B. This lookup still finds the channel inside collapsed B; .focus() cannot focus it, and removing Dismiss loses keyboard focus instead of invoking the fallback.
Check visibility on the exact target before accepting it, as the nearby lifecycle focus handler already does, and add this case to the browser regression. Alternatively resolve the actual rendered placement, while preserving the synchronous optimistic-snapshot behavior of Move. This edge is traced from source, not browser-reproduced; the new browser case covers healthy preferences only.
wesbillman
left a comment
There was a problem hiding this comment.
Star Lord, automated source reviewer, commenting via Wes’s GitHub account.
No new actionable findings in this focused follow-up. Reviewed head 06b29f21d3e559dcfaff32632c6182351d186c70 against base/merge-base 30c3589ba57ce926ac19c5526d8b87cde35d6e48, concentrating on the delta since the previously reviewed d75d0d31148be22053003500e61ce98128eb389a. This is not a fresh exhaustive audit of the entire sidebar feature.
- The sole new change restores
src/features/relay/broker.integration.test.tsbyte-for-byte to the base version (blob2171434a8404a6666fcc784e2dafdcbbba5b421c). Sidebar production code and its regression tests are unchanged since the previous review. - The restored comparison between two real-clock publish durations remains timing-sensitive: holding the first send does not guarantee it takes longer than the second. It is inherited base behavior, not a new sidebar regression; I am not reopening the unrelated timing repair in this feature. Controlled-clock attribution coverage remains in
traffic.integration.test.ts:190–239; that does not prove the IPC test cannot flake. - The existing non-blocking P3 hidden-row focus comment is unchanged, not resolved or duplicated by this review. Accepted persistence/product limitations are also unchanged.
Validation limits: source analysis only, using pinned Git objects with byte-verified extracts; no dirty source inputs and git diff --check passed. No tests, builds, app launches or CI reruns. The one-time check snapshot at 18:19 UTC shows run 36166318943 associated with this head: JavaScript and CI required failed; WebKit shard 1/2 was cancelled; the other three browser shards, measurements and Rust/tool integration passed; Windows was skipped. JavaScript job logs were unavailable to this review, so failure causation and the tested merge tree are not established. Exact-head runtime reliability, current-main integration and packaged/live-relay behavior remain unverified. This COMMENT is neither approval nor a merge-readiness verdict and does not alter earlier reviews.
Preserve active personal-group schema, exclusive placement, optimistic rollback and retry, legacy icons, and stable startup presentation. Keep healthy personal groups visible when legacy reads fail without enabling unsafe writes. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Share the existing one-shot account preference recovery with personal-group reads. Keep caller cancellation, cache clear, teardown and channel-content reads outside recovery. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Check live move admission before closing Create new. Preserve the name and group identity on rejection and offer in-dialog preference retry; accepted writes keep their quiet optimistic flow. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Use the shared decorative 14px icon slot and retain the submenu chevron. Extend the existing browser geometry check without adding a journey. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…ions Keep preference-read failure and retry visible after a mute-only confirmation, including a stale concurrent recovery read. Retain mainline action eligibility, empty DMs entry, deterministic personal saves and off-page grouping coverage after the rebase. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Keep mainline refresh behavior while composing Move and Sort over confirmed preferences. Exercise both save orders, rollback, cancellation, stale recovery and the built-app save/reload boundary. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Collect the bounded assignment/star request as bytes before decoding UTF-8. Exercise one-byte broker chunks and verify both the confirmed response and decrypted published group name, with oversized byte-budget rejection on both routes. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Publish stale-source and unavailable retry errors in the retained move notice. Disable retry while preferences cannot be written and explain the recovery action. Cover both group-source transitions and read recovery through the real sidebar and preference store. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Expand the current placement and hand focus back to the channel row after dismissing a failed move. Fall back to a visible row or sidebar control if the original channel disappeared. Add a real Chromium/WebKit keyboard journey covering a collapsed group and refreshed roster omission. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Match exact optional property semantics in the retry-notice fixture. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
3c0d62e to
4a6fc5e
Compare
wesbillman
left a comment
There was a problem hiding this comment.
Star Lord’s automated source review via Wes’s GitHub account.
No new actionable findings in this integration follow-up. Reviewed head 4a6fc5ed0d3000c272a5aa2790cec11230a7abc3 against target base 48c9a11da2e732af96b620640d02abb3aae0cf5d (merge base 64be4c2adf21e9920d4a7666354ceab1d784dd44), focusing on changes since the previously reviewed 06b29f21d3e559dcfaff32632c6182351d186c70. This is not a fresh exhaustive audit of the entire 41-file feature.
- The Create-new handoff now crosses the memoized row boundary:
ChannelSidebarItem.tsx:210–214forwards close completion, andChannelSidebar.tsx:658–675,1074–1076opens the pending dialog while suppressing the competing row-focus return. The shared menu wrapper and existing Create/cancel/focus browser journey were inspected as source, not executed. - The relocated work-session constructor retains the incoming verified discovery reader (
session.ts:904–928), including the distinct discovery contract inwork-sessions.ts. The preference store, personal-group adapter, Create dialog, and grouping browser journey remain byte-identical to the previously reviewed head. The unrelated model-picker and broker integration tests now match the target base. - The existing P3 hidden-row focus comment remains unchanged: the exact-target lookup still precedes the visible-row fallback at
ChannelSidebar.tsx:679–691. It is not resolved or duplicated by this review. Accepted whole-record/cross-record persistence limitations remain unchanged.
Validation limits: pinned Git-object analysis, range-diff comparison, 20 byte-verified source extracts, and git diff --check passed; no dirty source inputs. No PR code, tests, builds, app launches, installs, or CI reruns were executed. The one-time exact-head CI snapshot showed JavaScript, measurements, Chromium shard 2/3, DCO and security checks passing; five browser shards and Rust/tool integration were still running, and Windows was skipped. This is not a terminal CI result or evidence that the changed menu/dialog workflow passed. Browser/native focus behavior, current-target merged-tree acceptance, live-relay behavior and human acceptance remain unverified. Non-blocking COMMENT only—not approval or merge authorization.
Overview
Category: new-feature
User Impact: Users can move channels into saved groups or Starred, create a group directly from a channel, and recover failed saves without losing their place.
Problem: Saved sidebar organization is visible but cannot be changed directly from a channel, and moving between group stores must not lose existing preferences or create duplicate rows.
Solution: Add one Move channel menu with saved destinations, Create new, and exclusive Starred placement, backed by quiet optimistic saves and explicit recovery. Preserve the active personal-group schema and its existing encrypted writer, keep healthy personal groups visible when legacy reads fail, and inherit the parent-menu foundation’s fully rounded shared rows.
Scope and persistence boundaries
30c3589b; newer main is not yet integrated. Historical validation below is explicitly scoped to its named snapshots.Changes
File changes
dev/relay-broker.mjs
Expose bounded host-only assignment and star commands. Serialize writes per relay, sign authenticated requests, and require a fresh confirmation before reporting success.
dev/sidebar-create-section.test.mjs
Cover create-and-assign preservation, input limits, unknown publication outcomes, idempotent retry, and refusal to seed after a failed read.
dev/sidebar-group-moves.test.mjs
Exercise the session store with production encrypted preference writers, including partial failures, concurrent refresh, cancellation between records, and durable retries. Preserve main’s always-present DMs entry point in section assertions.
dev/sidebar-preference-writes.test.mjs
Verify signed broker round trips and reject invalid intents or foreign origins before upstream work.
dev/sidebar-preferences.d.mts
Describe the existing JavaScript preference decoder and new narrow assignment writer at the TypeScript boundary.
dev/sidebar-preferences.mjs
Apply one assignment or create-and-assign intent to a fresh verified encrypted head while retaining unrelated fields. Confirm retained state and make retries idempotent.
dev/sidebar-stars.mjs
Write explicit star/unstar intents with monotonic timestamps, preserved tombstones, bounded decoding, and fresh confirmation.
dev/sidebar-stars.test.mjs
Cover star validation, encryption, preservation, conflicting confirmations, no-op writes, and read/publication failures.
src/bundled/channels/Channels.module.css
Remove the old section-icon styles now owned by the dedicated group-icon component.
src/features/channel-navigation/ChannelSidebar.test.tsx
Cover both active-group-source transitions and unavailable-preference retry recovery through the real store and notice.
src/features/channel-navigation/ChannelSidebar.tsx
Wire Move channel with a decorative 14px outlined folder icon, Create new, exclusive Starred placement, focus restoration, and retained retry UI into main’s persistent sidebar owner. Preserve New session as the first independently eligible action; Move/Create also work off Messages and with Sessions disabled. Keep healthy personal groups visible during legacy-read failure while writes remain disabled.
src/bundled/channels/CreateSidebarSection.module.css
Keep the selected channel label on one truncated line within the create dialog.
src/bundled/channels/CreateSidebarSection.test.tsx
Verify a rejected draft retains its name and group identity through failed/successful preference recovery, and accepted submissions cannot be duplicated.
src/bundled/channels/CreateSidebarSection.tsx
Provide the shared-control create-and-move dialog with a stable retry identity and the active schema’s name limit. If preferences become unavailable before submission, retain the draft and offer in-dialog recovery rather than closing without an accepted move.
src/bundled/channels/SidebarGroupIcon.module.css
Give group icons a stable, bounded slot and loading placeholder without overlapping labels.
src/bundled/channels/SidebarGroupIcon.test.tsx
Cover custom media resolution, failed or missing icons, literal icons, and source/session changes.
src/bundled/channels/SidebarGroupIcon.tsx
Resolve legacy custom icons through session-owned media and use a stable placeholder rather than leaking raw shortcodes.
src/bundled/channels/SidebarSectionIcon.tsx
Remove the superseded icon component; its caller uses SidebarGroupIcon.
src/bundled/channels/useChannelRowMenu.ts
Pass rendered placement to the action composer while preserving main’s stale-menu retirement and item-count eligibility.
src/bundled/channels/useSidebarPreferences.ts
Expose session-owned mutation, writability, and retry commands to the sidebar view.
src/features/channel-templates/capability.test.ts
Cover cancellation before preparation/enqueue and retained durable delivery after enqueue.
src/features/channel-templates/capability.ts
Accept an optional operation signal during save preparation while leaving enqueued delivery owned by the existing outbox/session.
src/features/relay/session.ts
Compose active-store routing and refresh with the existing catalog and preference owners. Share the existing one-shot account-read recovery with catalog reads after roster invalidation, excluding caller cancellation, cache clear, teardown, and channel-content reads.
src/features/relay/sidebar-moves-store.test.ts
Verify immediate placement, serialized records, protection against older failures, retained retry, and stable create identities. Cover both Move/Mute and Move/Sort save orders, failed operations, cancellation/cache-clear/disposal, pending overlays and a field-only confirmation completing during failed preference recovery.
src/features/relay/sidebar-personal-groups.test.ts
Exercise schema-preserving personal-store saves, source changes, template-default preservation, startup cancellation recovery, and cancellation/cache-clear boundaries.
src/features/relay/sidebar-personal-groups.ts
Select the explicitly activated group store and reuse the existing recipe writer for personal assignments. Reject stale-source intents rather than migrating or writing hidden legacy groups.
src/features/relay/sidebar-preferences-store.test.ts
Cover retained confirmed state, mutation/refresh races, session isolation, teardown, explicit star changes, and write gating throughout failed reads and retries.
src/features/relay/sidebar-preferences-store.ts
Own optimistic placement and serialized persistence in the relay session. Keep one confirmed preference state beneath pending Move and Sort overlays; each confirmation updates only its fields and cannot erase a failed full-read retry. Preserve main’s refresh-during-sort behavior and confirmed-only mute policy; fence deferred catalog refresh across clear/disposal.
src/features/relay/sidebar-preferences.test.ts
Keep exact legacy decoder/security assertions while modeling the separate personal-catalog lookup used for active-store selection.
src/features/relay/sidebar-preferences.ts
Define narrow assignment/star mutation contracts and an in-memory active-source marker without changing the persisted legacy schema.
src/features/relay/sidebar-sorting-store.test.ts
Adapt the constructor call to the composed owner without changing main’s sorting assertions.
src/features/relay/transport.ts
Expose capability-gated broker mutation calls and validate returned preference projections.
src/features/relay/work-sessions.ts
Require the writable outbox for generic saved-record delivery confirmation rather than channel-creation authority. Preserve main’s channel-addition capability, cancellation, and retry checks.
src/features/relay/work-sessions.test.ts
Cover saved-record confirmation when channel creation is unavailable.
tests/browser/fixture.mjs
Add opt-in icon data and grouping publication retention to main’s signed personal/sorting fixtures. Compose sort/group/star/mute/read handling and exact injected-failure accounting without duplicate handlers; retain distinct personal-group IDs and keep default fixtures small.
tests/browser/navigation-group-icons.spec.mjs
Verify actual browser media loading, non-overlapping icon geometry, missing-icon presentation, and single-line dialog layout.
tests/browser/navigation-groups.spec.mjs
Exercise real menu/dialog keyboard behavior, optimistic move/create/star, partial-failure recovery, encrypted persistence and reload, both schema paths, and independent personal-group presentation on legacy failure. Preserve main’s persistent-sidebar navigation/retention journey; add off-page Create/Move coverage with Sessions disabled. Explicitly dismiss the independently eligible New session menu before recovery, and control the personal record clock instead of relying on elapsed interaction time. Extend the existing menu/reload journey with a held Sort followed by Move, checking pending placement and persistence of both after reload; no additional browser case.
docs/channels.md
Describe persistent-sidebar ownership, independently eligible actions, one confirmed snapshot under Move/Sort overlays, recoverable full-read errors and confirmed-only mute policy.
tests/browser/channel-lifecycle.spec.mjs
Preserve existing lifecycle behavior assertions while expecting Move alongside New session and attention actions, without adding a lifecycle separator before authority resolves.
tests/browser/navigation-mute-read.spec.mjs
Preserve main’s mute/read journey, including Sessions toggling, while expecting the independently available Move action.
Reproduction Steps
Validation
Current review follow-up at
d75d0d31a7e0ba04: replace the retained error after a stale-source/non-writable retry rejection; preserve the write guard and disable Retry with recovery instructions when preferences are unavailable.d75d0d31corrects the test fixture’s exact-optional-property typing.3cce8844: Dismiss expands the channel’s current placement and restores focus, falling back to a visible row/sidebar control if it disappeared.navigation-groups.spec.mjsjourneys pass (50.3s), including collapsed-placement dismissal and upstream roster omission → surviving-row focus. Run ata7e0ba04plus the changes committed unchanged in3cce8844; laterd75d0d31changes only the mounted-test fixture, not production/browser code.d4722510: production +37/−11, tests +157/−2. No persistence format or menu redesign.80511fa8introduces a 200 ms hold but retains the independentsecond.duration < first.durationassertion previously removed here; integration is paused rather than silently discarding that mainline assertion.Historical validation — superseded snapshots, not current merge status
Historical rebase at
0b4e99484be36e5bb766ef2a3fe621439829fca530c3589ba57ce926ac19c5526d8b87cde35d6e48. Preserves main’s persistent sidebar, startup/activity/scroll settlement, New session, sorting, mute/read/lifecycle, shared styling and user-status integration. Unmerged sibling follow-ups are not silently imported.BUZZ_TEST_WORKERS=2. No hook bypass. An earlier full-package run during conflict integration passed 3,671 tests on75d681e6plus integration edits; that is historical, not the final-head result.CI required, DCO and approving/code-owner review. No CI-green or merge-readiness claim.752aaacc): the assignment/star request body collector indev/relay-broker.mjsstill usesraw += part, decoding incoming chunks separately. The earlier reproduced UTF-8 group-name corruption is not repaired by this rebase. Collect bytes, decode once, and add a broker chunk-boundary regression before merge.bin/pnpm test:browser --project chromium --project webkit --no-deps \ tests/browser/navigation-groups.spec.mjs \ tests/browser/navigation-sorting.spec.mjs \ tests/browser/navigation-group-icons.spec.mjs \ tests/browser/navigation-mute-read.spec.mjs \ tests/browser/channel-lifecycle.spec.mjs \ tests/browser/navigation.spec.mjs \ tests/browser/navigation-sidebar.spec.mjs \ tests/browser/navigation-session-menu.spec.mjs \ tests/browser/navigation-scroll-intent.spec.mjs \ tests/browser/sidebar-unread.spec.mjs \ tests/browser/user-status.spec.mjs tests/browser/presence.spec.mjs bin/pnpm test:browser tests/browser/channel-opening.spec.mjs \ --project '*-measurements' --no-deps --workers=1Historical folder icon at
730c4b6Added Phosphor
FolderSimpleIconin the existing decorative 14px leading slot for Move channel, retaining its accessible name, chevron and behavior. The existing icon journey gained geometry/accessibility assertions; no icon-only browser case was added. Its 16/16 Chromium/WebKit checks and then-current 2,697-test push gate passed. Later integrations supersede that head’s status; the gallery below is pinned tod4722510.Historical parent-menu alignment at
169616f9408788bcd592080397fac5c2785e2da088b1bc9. Superseded by the current integration: feat(channels): move session creation into the context menu #209 has landed and its parent-menu behavior is now inherited from main.752aaacc): the new broker body collector decodes HTTP chunks separately and can corrupt a multibyte group name (Launch 🚀reproduced asLaunch ����with HTTP 200). This styling-only update does not fix it. Collect bytes and decode once, with a broker regression, before merge.d73da62plus these four file edits; commit hooks made no changes.169616f: 263 files / 2,697 unit tests, TypeScript and design types/guards passed. DCO is green; hosted CI was queued/running at publication. No polling or CI-green claim.8f109a6; the gallery below now supersedes them withd4722510captures.Historical review follow-up at
d73da62a42da0fd5cc4e0816ad0ecc03b55f893921ea351.d73da62, with the admission-race blocker resolved. A subsequent scope audit found the separate UTF-8 blocker documented above; this historical verdict is not current merge clearance. This is an agent review reported in the originating work thread, not a GitHub approval. Hosted checks remain pending; the PR stays draft.Integrated baseline at
2232d43Validated on clean head
2232d43bb61d18d68d309b5e923f695ab5007b34:git diff --check origin/main...HEADpassed; remote branch and local head match.Browser commands (after the standard browser build/setup):
Browser coverage and fail-then-pass evidence
574ba24initially passed 52/56 browser checks but failed four Chromium grouping cases after roster discovery cancelled the catalog read.2232d43shares the existing narrowly guarded recovery and adds session regressions; all 56 then passed. No browser retries or weakened assertions were used to mask it.Remaining gaps
752aaacc; the existing PR is no longer draft. Screenshot capture does not establish hosted-CI success, reviewer approval, live-relay/native acceptance, or merge readiness.Screenshots / Demos
Captured September 25, 2026 at
d47225108363093987d31d0cc5ed5c9966d9f328— 27 images. These replace the old8f109a6gallery. Actual production-built app and production broker in Chromium, using ephemeral fixture identities and a modeled, signed upstream relay. Dark appearance uses the app’s real palette/teal shell; no fabricated UI, component showcase, injected CSS, or live user data.What belongs to this PR: Move channel, saved destinations/Create new, exclusive Starred placement, and their safe persistence/composition. Already merged siblings shown for context: New session, Mute/Unmute, Mark as Read/Unread, Archive/Delete/Leave/DM Hide, and section Sort. Starred is a destination inside Move—not a separate root-menu action. Sort belongs to the section menu—not the channel row.
“Attention” means Mute/Unmute and Mark as Read/Unread, depending on state. These are fixture-specific eligible menus, not a grant of authority for every channel or host.
Integrated channel menu
01 · Sole-owner stream: the complete merged order—New session, Move channel, attention, then lifecycle. Leave is correctly absent for the sole owner.
Grouping, Starred, Create and saved placement — 7 images
02 · Ungrouped stream. Move offers Starred, Work and Create new; there is no removal action before assignment.
08 · Assigned to Work. Work is checked and Remove from Work is available; the merged parent-menu siblings remain visible.
09 · Assigned to Starred. Starred is checked and Remove from Starred is available. The selected channel has one rendered placement.
10 · After removing Starred. Launch planning returns to Channels, not its previous Work group. Alpha is a separate fixture channel that remains starred.
11 · Empty Create dialog. Create and move is disabled until a name is supplied.
12 · Named Create dialog. The real input renders Launch 🚀 · Café · 日本語 and enables Create and move.
13 · Confirmed state after reload. Launch planning remains in the new Unicode-named group and appears once; Alpha remains exclusively Starred. The narrow sidebar truncates the long group label normally. This is rendering/reload evidence, not the separate HTTP-chunk regression.
Merged attention, session destination and section Sort — 3 images
03 · Attention state variants. After muting and marking the channel unread, the same menu offers Unmute and Mark as Read instead of Mute and Mark as Unread.
07 · New session destination. The inherited action opens the actual New session page. This isolated connection cannot send messages, as the visible composer notice states; no agent session execution is claimed.
14 · Section-level Sort. Work → More actions → Sort offers Recent and A–Z, with A–Z selected. This inherited section menu is separate from Move channel.
Lifecycle permission variants and confirmations — 7 images
15 · Member. Leave is present; Archive and Delete are absent.
17 · Admin. Archive and Leave are present; Delete is absent.
18 · Owner with another owner. Archive, Delete and Leave are all present.
04 · Archive confirmation. The dialog explains that the channel is archived for everyone, messages are retained, and unarchive requires another supported client. Cancelled after capture.
05 · Delete before name confirmation. The destructive action is disabled with an empty confirmation input.
06 · Delete after exact name match. Entering Launch planning enables Delete channel. Cancelled; no deletion was published.
16 · Leave confirmation. The dialog describes sidebar removal and possible invitation requirements for rejoining a private channel. Cancelled after capture.
DM and forum type boundaries — 3 images
19 · DM menu. Mute, Mark as Unread and Hide conversation; neither Move nor New session is offered.
20 · DM Hide confirmation. The dialog explains that hiding affects this viewer’s sidebar, retaining messages and membership. Cancelled after capture.
21 · Forum menu. The sole-owner forum retains New session, attention, Archive and Delete, but excludes Move. This does not extend stream-only grouping to forums.
Recovery, personal groups and Sessions-disabled composition — 6 images
22 · Failed Move and retained retry. A deliberately injected assignment-endpoint 502 rolls Beta back to Work. The real UI warns about possible partial saves and offers Retry move and Dismiss; this is an intentional fixture failure, not a live incident.
23 · Retry confirmed, then reloaded. After removing the injected failure and retrying, Beta remains in Channels after reload.
24 · Activated personal-group schema. Personal work is selected, with Remove from Personal work and Create new. The active store is preserved, not migrated.
25 · Personal-group Create dialog. The same dialog has no icon field. The capture journey also checked the input’s existing maxlength=120; the screenshot alone is not evidence of that DOM attribute.
26 · Fail-closed lifecycle lookup. Beta is an intentional non-UUID fixture channel, so lifecycle permission validation fails. Channel actions unavailable and Retry channel permissions replace lifecycle actions; independently eligible Move/attention/session actions remain. This is not a successful authorization example.
27 · Grouping remains independent of Sessions. Captured from Projects after disabling Sessions. New session is absent while Move/Starred/Create and eligible attention/lifecycle actions remain; the crop focuses on the sidebar menus.
Capture evidence and limits: 11/11 isolated Chromium capture journeys passed in 25.9s, one worker, no retries. No checked-in browser cases or product files were changed for this refresh. Archive/Delete/Leave/Hide dialogs were cancelled; there were no live lifecycle operations. Grouping writes and recovery were exercised only against the modeled upstream. These images do not certify native hosts, live-relay lifecycle completion, cross-device convergence, or complete error/host combinations. Existing whole-record last-write-wins limitations are unchanged. The historical multi-engine validation above is attributed to its own commits, not relabeled as a new run.
Implementation and this description prepared by Carl (AI agent) under the authorizing maintainer’s direction.