Skip to content

[#233] Unified workspace left rail: single host-tagged room list, 1/3-2/3 split#237

Merged
realproject7 merged 3 commits into
mainfrom
task/233-unified-left-rail
Jul 13, 2026
Merged

[#233] Unified workspace left rail: single host-tagged room list, 1/3-2/3 split#237
realproject7 merged 3 commits into
mainfrom
task/233-unified-left-rail

Conversation

@realproject7

Copy link
Copy Markdown
Owner

Closes #233
Parent: #77 — Agent Gather Platform Pivot. Depends on: #221/#222/#210/#217. Enables: #234.

Unify the workspace left rail into a single host-tagged boardroom list with a 1/3–2/3 navigation split, so a user in many boardrooms switches from one place. Dashboard-only (src/browser/shell.*); src/cli/* untouched.

EPIC Alignment

  • This ticket's role (from Unified workspace left rail: single host-tagged boardroom list with 1/3-2/3 navigation split #233): unify the workspace room rail and preserve joined-room controls while keeping the shell's stable two-region navigation model.
  • Contracts respected/exposed: ONE token-free room list; hosted-first ordering with compact host tags; a ~34%/66% independently scrolling rail; room.channels rendered when supplied with an unchanged #general fallback.
  • Out-of-scope respected: no new control-plane fetch and #234 is NOT a prerequisite — renderChannelNav renders room.channels when the payload already carries them and otherwise falls back to #general, exactly as today. No change to room semantics, storage, joined-room metadata shape, or src/cli/* (Launcher UX: make bare agentgather open the local dashboard #232 launcher surface).
  • Reuse-first: reuses the existing applyOverflow/toggleOverflow collapse pattern (now parametrized per-section: rooms=6, channels=8), the existing enterRoomState/goHome two-region swap, and the existing joined-row builder + #210/#217 handlers — the merged list routes both hosted and joined data through one renderer (renderRail).

Self-Verification

Diff re-read adversarially; kill-list scanned; acceptance criteria matched 1:1.

Acceptance criteria → evidence (new tests in test/browser-platform.test.ts):

  • One list; the section split is gone; host rows carry a host tag; joined rows keep reachability → the unified rail merges hosted + joined into one host-tagged list, hosted first (asserts .joined-section/#joined-list count 0, one #room-list with 2 .room-row + 2 .joined-row, 2 host tags, hosted-before-joined order, joined ordered by recent activity).
  • ~1/3–2/3 split, own scroll per area, view-more expands with no layout jump and never hides the other section → the rail keeps a ~1/3-2/3 split and view-more expands within the rooms scroll (rooms region ratio 0.28–0.4, lower ≥0.55, both overflow-y:auto, rooms overflows its cap, expanding 12 rows leaves the lower region's top unchanged).
  • Selecting any merged-list room enters the room state → same test asserts channel nav + breadcrumb after selecting a hosted row.
  • Archived toggle, remember form, archive/delete still work → existing #210/#178/#216 joined tests pass against the merged list (selector updated only where the container moved: #joined-list#room-list).
  • Channel nav renders room.channels (name, #, type) and falls back to #generalchannel nav renders room.channels from the payload and falls back to #general.
  • Per-section caps → channel nav caps at 8 with a view-more control (channels=8); rooms=6 covered by the merged-list split test + the existing overflow test.
  • No raw token/invite/card URL in DOM/storage/requests → re-asserted (.room-rail innerHTML token-free; existing token-free joined tests still pass).

Verification suite (all green): pnpm build, pnpm lint, pnpm typecheck, pnpm kit-guard (4 panes clean), pnpm no-stub.
Tests: browser-platform 32 pass / 0 fail (4 new #233 + 28 preserved); browser-boardroom-shell 3 pass (room-page surface, unchanged). Full suite: 409 pass / 3 fail — the 3 (active-session, cli-brief-file, cli-room: "participant token is not allowed") are in src/cli/commands/room, disjoint from this browser-only change, and reproduce identically on the base; unrelated/pre-existing.

Design Fidelity

Per the #218 shell spec (shell-workspace-spec.md): grid/[hidden] rules and design-kit-tokens-only preserved; #233 restructures only the top rail.

Element Spec / #233 intent Implemented Evidence
Top rail ONE merged list, hosted first + host tag on host rows single #room-list; buildHostRow adds .room-tag[data-tag="host"]; joined rows follow, recent-first merge test
Joined affordances reachability + #210 archive/delete + #217 remember preserved .joined-reach, archive/delete controls, #joined-add form (moved to .rail-tools), archived toggle #210/#178/#216 tests pass
Rail split ~34% rooms / ~66% lower, own scroll each existing .rail-rooms (flex:0 1 34%) / .rail-lower (flex:1 1 66%) kept split test (ratios + overflow-y:auto)
View-more rooms=6, channels=8, expand within section, no layout jump applyOverflow(list, btn, limit); #rooms-more/#channels-more split + channels-cap tests
Channel nav room.channels when present, #general fallback renderChannelNav(room) reads room.channels, filters renderable, falls back channels test
Tokens design-kit tokens only, no raw literals .room-tag uses --accent-line/-weak/-text, --r-pill kit-guard green

Security

Invariant (#233) How it holds Evidence
Joined metadata stays token-free; no new fetches needing a participant token merge is pure client-side rendering of existing /rooms + /joined-rooms payloads; joined open href is the loopback /joined-rooms/open redirect (no token) .room-rail innerHTML token-free; existing token-free joined tests pass; no new fetch added
No raw token/invite/card URL in DOM, storage, or requests host tag is the static word "host"; channel nav renders name/type only merge + channels tests assert /tgl_|token=|Bearer/ absent
Design-kit tokens only — no raw color literals new CSS (.room-tag, .rail-tools) uses tokens pnpm kit-guard (4 panes clean)
No new runtime dependencies none added package.json unchanged

Deviations

  • browser-boardroom-shell.test.ts left unchanged (still passing): it exercises the room page (room.html / #channel-rail), a surface disjoint from this dashboard-only rail refactor. The dashboard channel-nav change is covered by the new browser-platform.test.ts tests. Extending the room-page suite would be artificial — flagging rather than fabricating coverage.
  • Joined #joined-empty note shows whenever no joined rooms are tracked (even if hosted rooms exist) — it is the joined-room hint (paste-an-invite), and hosted rooms don't suppress it.
  • Out-of-scope working-tree files (AGENTS.md, CLAUDE.md, DESIGN-GUIDE.md, .gitignore) present before this task were left uncommitted.

…3 split

Merge the "Your rooms" (hosted) and "Rooms I'm in" (joined) sections into ONE
top-rail list: hosted rooms first, each with a compact `host` tag, then joined
rooms by most-recent activity. Joined affordances move with the list —
reachability, #210 archive/delete, #217 remember form — nothing removed. The
existing ~34%/66% flex rail (own scroll per region) is preserved; the merged
list caps at 6 and the channel nav at 8 behind their own view-more controls.
Channel nav now renders room.channels ({id,name,type}) when the control-plane
payload provides them, keeping the #general fallback. Token-free throughout;
design-kit tokens only; no new fetches or dependencies.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns 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.

Verdict: REQUEST CHANGES

Epic Alignment: FAIL

The ticket/head dispatch restrict this work to shell.html, shell.js, and focused tests, but the PR changes shell.css.

Checked (evidence)

  • Structural gate: PR body includes filled EPIC Alignment, Self-Verification, Design Fidelity, Security, and Deviations sections.
  • Ticket/epic/design context: gh issue view 233; gh api repos//issues/77; shell-workspace-spec.md; DESIGN-GUIDE.md.
  • Fidelity spot-check: merged render at src/browser/shell.js:234; host tag at :288; preserved joined controls at :1167; channel fallback at :449; caps at :478.
  • Riskiest part of this diff: merged rail lifecycle; joined rows still use the existing device-local controls and loopback open path.
  • Kill-list: scanned full diff - scope breach, off-scale UI values, and new one-call-site helpers.
  • CI: gh pr checks 237 -> Release gates pending.

Findings

  • [blocking] The PR changes an excluded file surface.
    • File: src/browser/shell.css:430
    • Why it fails: #233 and @Head's dispatch restrict changes to shell.html, shell.js, and the named tests; shell.css is outside that contract.
    • Do instead: reuse an existing design-kit class/token treatment from the permitted surface, or obtain an explicit scope amendment from @Head before retaining this CSS change.
  • [blocking] The new all-caps host tag violates DESIGN-GUIDE scale rules.
    • File: src/browser/shell.css:1325
    • Why it fails: padding 1px 7px is off the 4px grid; 9.5px is below the 10-12px UI-label range; uppercase text uses 0.04em instead of the required >=0.06em tracking.
    • Do instead: use grid-aligned chip spacing, a 10-12px label size, and >=0.06em letter spacing through existing design-kit tokens/classes.
  • [blocking] New helpers each have exactly one call site.
    • File: src/browser/shell.js:260
    • Why it fails: buildHostRow (:239), joinedActivity (:252), isRenderableChannel (:450), and buildJoinedRow (:253) are new single-use abstractions, prohibited by the kill-list.
    • Do instead: inline each into its caller; retain the existing behavior and tests.

Decision

The merged-list and channel-nav direction aligns with #233, but these mandatory contract and fidelity failures must be resolved before approval.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE ✅ (#233 unified workspace left rail)

Reviewed head 4710690 vs fresh origin/main ea85cbe. Dashboard-only diff — src/browser/shell.html / shell.js / shell.css + test/browser-platform.test.ts. src/cli/* and the room page untouched. Scope matches the ticket.

Checked (evidence)

  • Merged single list, hosted first + host tag: renderRail() appends buildHostRow (control-plane) then buildJoinedRow (device-local, showArchived-filtered, sort((a,b)=>joinedActivity(b)-joinedActivity(a))). Old .joined-section/#joined-list gone. Test asserts #room-list count 1, 2 .room-row + 2 .joined-row, li kinds [host,host,joined,joined], joined order [Recent Join, Older Join], host tag only on hosted rows.
  • Joined affordances preserved: buildJoinedRow keeps reachability badge, Dashboard: archive and delete device-local joined-room entries #210 archive/delete controls, keyboard role=link open, confirming-guard; Fix dashboard remember for tokenized invite links #217 remember form + honest empty note relocated to .rail-tools; archived toggle moved to .rail-head, still wired (renderJoinedrenderRail).
  • ~34%/66% split with real work: existing .rail-rooms(flex 0 1 34%)/.rail-lower(flex 1 1 66%) unchanged; test asserts computed roomsRatio 0.28–0.4, lowerRatio ≥0.55, both overflow-y:auto, rooms region actually overflows its cap.
  • Overflow caps + no layout jump: applyOverflow(list, btn, limit) parametrized — merged list cap 6, channel nav cap 8; with 12 rooms only 6 visible + "show 6 more", expanding reveals 12 and the lower region top is byte-identical (asserted lowerTopAfter === lowerTop). CSS .room-list > li.is-collapsed now covers both hosted li and .joined-row.
  • Channel nav: renderChannelNav(room) renders room.channels filtered by isRenderableChannel (id+name strings, type defaults chat) when provided, else #general fallback — no new fetch, Control plane: token-free channel metadata for hosted rooms #234 not a prerequisite. Test covers both paths.
  • Token-free: joined open href is the loopback /joined-rooms/open redirect; loadJoinedRooms uses the existing token-free ./joined-rooms endpoint (no new participant-token fetch); .room-rail innerHTML asserted free of tgl_/token=/Bearer.
  • Design-kit tokens only: .room-tag uses --accent-line/weak/text + --r-pill; kit-guard 4 panes clean. [hidden] behavior and the shell grid untouched (no remount).
  • No new deps: package.json unchanged.

Design Fidelity

Ticket requirement Verdict Evidence
One merged host-tagged room list, hosted first #room-list single, [host,host,joined,joined]
Joined by most-recent activity joinedActivity sort, [Recent Join, Older Join]
All joined affordances preserved reachability/#210/#217/archived toggle intact
~34%/66% split, own scroll each computed ratios 0.28–0.4 / ≥0.55, both auto
Caps rooms=6 / channels=8, view-more in-section, no jump 6 visible/12, lower top unchanged
room.channels + #general fallback both paths tested
Kit tokens only, no raw color kit-guard clean

Independently verified (fresh worktree @ 4710690)

typecheck clean · build ok · lint · kit-guard 4 panes clean · no-stub · browser-platform + boardroom-shell 35 pass / 0 fail · CI Release gates PASS (run 29237668542). The 3 full-suite failures are in src/cli/commands/room ("participant token") — untouched by this browser-only diff, pre-existing, no causal link.

Non-blocking notes (no change required)

  • railTitle count is rooms.length + joinedRooms.length (all joined, incl. archived), so the header total can exceed visible rows when archived are hidden — cosmetic "total tracked" reading.
  • Activity ordering keys on lastSeen/joinedAt; a fromCli joined entry lacking both degrades to insertion order (NaN-guarded → 0). Browser-remembered entries (dominant path) carry them, and the test confirms ordering.

No blocking findings — APPROVE. (Formal GitHub approve blocked by shared-identity self-PR; this comment is the verdict of record.)

…line helpers

- shell.css now differs from base by ONLY the authorized `.room-tag` host-badge
  rule (ticket: "compact host badge, design-kit tokens only"). Reverted the
  `.rail-tools` rename and the collapse-rule edit; the joined tools wrapper reuses
  the existing `.joined-section` class, and `.room-list > li.is-collapsed` already
  covers the merged joined rows.
- Host tag snapped to the kit chip scale: padding 2px 8px, font-size var(--fs-xs),
  letter-spacing 0.06em (mirrors `.joined-reach`) — no off-grid/hardcoded values.
- Inlined the new single-caller helpers (buildHostRow, buildJoinedRow,
  joinedActivity, isRenderableChannel) into renderRail/renderChannelNav, restoring
  the original inline-row pattern. Behavior-identical; 35 browser tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns 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.

Verdict: REQUEST CHANGES

Epic Alignment: PASS

The amended CSS surface is now limited to the authorized host-tag rule, and the merged-rail implementation remains aligned with #233.

Checked (evidence)

  • Delta reviewed: 4710690..3c6380b.
  • Prior scope finding resolved: base comparison shows shell.css changes only the host-tag rule.
  • Prior helper finding resolved: buildHostRow, buildJoinedRow, joinedActivity, and isRenderableChannel are removed from the delta.
  • Prior fidelity check: host label now uses --fs-xs and 0.06em uppercase tracking at src/browser/shell.css:1344.
  • Riskiest part: the inlined merged-row render path; joined controls and the token-free loopback open path are preserved in renderRail.
  • Kill-list: scanned new ranges - one remaining off-grid spacing value.
  • CI: gh pr checks 237 -> Release gates pending.

Findings

  • [blocking] Host-tag vertical padding remains off the required 4px spacing grid.
    • File: src/browser/shell.css:1339
    • Why it fails: the #233 scope amendment explicitly requires the compact chip to follow the 4px grid, but padding is 2px 8px.
    • Do instead: use 4px vertical padding with an 8px (or token-equivalent) horizontal value, then retain the current --fs-xs and 0.06em tracking.

Decision

The first-round scope and abstraction findings are fixed. Snap the remaining 2px padding to the agreed grid; then I can complete the re-review once CI is green.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE ✅ (re-review on 3c6380b)

Re-reviewed the delta 47106903c6380b (@re1 round-1 findings). All resolved:

  1. CSS surface shrunkshell.css now differs from base by only the 17-line .room-tag block (the ticket's authorized "compact host badge" exception); the .rail-tools rename and collapse-rule edit reverted, joined tools reuse .joined-section, and the existing .room-list > li.is-collapsed already covers the merged joined rows.
  2. Host-tag values on the kit chip scale — padding: 2px 8px, font-size: var(--fs-xs), border-radius: var(--r-pill), letter-spacing: 0.06em, accent tokens only.
  3. Helpers inlinedbuildHostRow/buildJoinedRow/joinedActivity/isRenderableChannel folded back into renderRail/renderChannelNav (0 refs); the emitted DOM is line-for-line identical to 4710690.

Verified fresh @ 3c6380b: typecheck clean · build ok · lint · kit-guard 4 panes clean · no-stub · 35 browser tests pass / 0 fail · CI Release gates PASS (run 29238262094). My 4710690 correctness/security verdict carries — the merged-list logic, token-free surface, 34/66 split, and overflow caps are behavior-identical.

On the open 2px vs 4px vertical-padding point (@re1 finding)

Recording my independent read with evidence, deferring the final call to @re1's design VETO:

  • The Unified workspace left rail: single host-tagged boardroom list with 1/3-2/3 navigation split #233 issue body carries no 4px-grid requirement — its design constraint is "design-kit tokens only, no raw color literals," which this satisfies (--fs-xs/--r-pill/--accent-*, kit-guard green).
  • .room-tag's padding: 2px 8px exactly mirrors its sibling chip .joined-reach (shell.css:533 base) and the dominant kit chip padding (2px 8px at base lines 533/568/616/1858). Forcing 4px vertical would make the host tag diverge from the very chip it sits beside.

So 2px 8px reads as the kit-faithful value to me and I don't block on it. If @re1 holds the strict-4px line as the design lead, snapping vertical padding to 4px is a trivial, also-acceptable change and my APPROVE carries to it either way — the gate just needs @re1 satisfied.

No blocking findings from RE2 — APPROVE. (Formal GitHub approve blocked by shared-identity self-PR; this comment is the verdict of record.)

…4px 8px)

Durable scope amendment: the compact host chip follows the 4px grid. Only the
`.room-tag` vertical padding changes; --fs-xs, 0.06em tracking, kit tokens, and
the host-tag-only CSS surface are retained. Behavior-identical; tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE ✅ (carries to 574daf2)

Delta 3c6380b574daf2 is exactly the single change requested: .room-tag padding: 2px 8px4px 8px (+ its comment), aligning the compact host chip to the durable #233 4px-grid scope amendment. shell.css still differs from base by only the authorized .room-tag block; --fs-xs, 0.06em tracking, --r-pill, and the --accent-* tokens are all retained. No behavior/DOM change — my 3c6380b verification (typecheck/build/lint/kit-guard 4-panes-clean/no-stub green, 35 browser tests pass) carries unchanged; padding is not asserted by any test and is outside kit-guard's color scope.

With the operator-confirmed durable amendment establishing the 4px-grid requirement, @re1's finding was correct and is now resolved. My independent read is settled on that basis.

CI Release gates re-running on 574daf2 (run 29246050937) — needs to settle green for @Head's lead-sign-off gate; prior 3c6380b run was green and this is a one-line CSS value.

No blocking findings — APPROVE. Hold for the lead sign-off gate per the #233 merge policy. (Formal GitHub approve blocked by shared-identity self-PR; this comment is the verdict of record.)

@project7-interns project7-interns 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.

Verdict: APPROVE

Epic Alignment: PASS

The final #233 delta stays within the amended host-tag CSS scope and preserves the unified, token-free workspace rail contract.

Checked (evidence)

  • Re-review range: 3c6380b..574daf2.
  • Prior findings resolved: host tag is now 4px 8px at src/browser/shell.css:1339; --fs-xs, token colors, and 0.06em uppercase tracking are retained.
  • CSS scope: live base comparison remains limited to the authorized .room-tag block.
  • Riskiest part of this diff: compact-chip dimensions; the one-line change meets the durable 4px-grid amendment without changing DOM or interaction behavior.
  • Design fidelity: merged list, joined controls, independent rail regions, per-section caps, and room.channels fallback were verified in the prior live diff and are unchanged in this range.
  • Kill-list: scanned new range - clean.
  • CI: gh pr checks 237 -> Release gates pass (2m42s, run 29246050937).

Findings

  • None.

Decision

All RE1 findings are resolved on live head 574daf2. Approved; merge remains subject to the stated lead-sign-off gate.

@realproject7

Copy link
Copy Markdown
Owner Author

Lead sign-off granted. Final head 574daf2 was independently inspected: Release gates are green; RE1 formally approved; RE2 carry approval covers the exact narrow padding delta; canonical sub-PO inspection passed. The final delta is limited to the authorized host-tag 4px by 8px adjustment and comment, retaining token-only styling and 0.06em tracking. Canonical sub-PO may merge under the Agent Gather lead/sub-PO protocol.

@realproject7
realproject7 merged commit 6ee4951 into main Jul 13, 2026
1 check passed
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.

Unified workspace left rail: single host-tagged boardroom list with 1/3-2/3 navigation split

2 participants