Skip to content

feat(workspace): close a chat from its tab, unread count on tabs, first-click star fix (ent#841, #3356) - #3358

Merged
vybe merged 5 commits into
devfrom
feature/3356-star-close-chat
Oct 8, 2026
Merged

vybe merged 5 commits into
devfrom
feature/3356-star-close-chat

Conversation

@trinity-ability

@trinity-ability trinity-ability commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What

Three Workspace changes in one PR.

1. Starring a chat works on the first click — Fixes #3356

Root cause. refreshThreads replaces the whole chat-state map with what GET /chat-state returns. A read already in flight when the star is clicked (the 20 s poll, a turn finishing) answers with the pre-click state and overwrites the optimistic star. The server has the star and the screen does not; the second click "works" because it re-sends the same PUT after the stale read has gone by.

Fix. Local star writes are held over any read issued before they settled (portalUtils.createWriteGuard). Only a read that started after the write settled retires the override.

Second defect found on the way: starring a never-used Main from the header filled the header star, but the sidebar filters an unused Main out, so nothing appeared under Starred. A starred unused Main is now listed.

2. Close a chat with an × on its tab — abilityai/trinity-enterprise#841

  • Every chat tab but Main has an × labelled "Archive chat". It is hover/focus-revealed from sm: up, always drawn on touch, keyboard reachable, and also on the rows of the "N more" menu. One click, no confirmation.
  • Closing archives and never deletes: PUT / DELETE /api/enterprise/client-portal/agents/{name}/sessions/{id}/archive. Roster- then session-scoped, uniform 404, idempotent, 60/min. The live Main is a named 409. No schema change.
  • "Chat archived · Undo" toast after a successful close. A refused close reverts the tab and shows an InlineError under the strip.
  • Closing the open chat moves to the tab on its right, else its left, else the agent page.
  • OverflowTabs gains an opt-in closable tab. A strip with no closable tab renders the same DOM as before.

Decisions recorded (asked of the operator, 2026-10-08):

  • One archive rule. Closing uses the same archived_at Reset's retired Main carries, and every archived chat leaves the tab strip and the sidebar's active groups for a collapsed Archived · N group. Opening a chat there reopens it. This reverses the ent#523 ruling that a Reset archive "becomes the newest tab". The chat on screen keeps its tab even when archived.
  • Starred chats can be closed. The star is untouched; the chat leaves Starred while archived and gets its pinned place back on reopen.
  • Reply in flight. The open chat's × is inert with a tooltip while its reply is in flight. A background chat can be closed mid-turn; the reply still lands in the archived chat.
  • Rooms are excluded. The strip lists one agent's 1:1 chats, so a room has no tab to close.

3. Unread count on the tab

A chat with unread replies that is not on screen shows the sidebar row's count on its tab in the same solid blue (badgeVariant: 'primary'), and in the "N more" menu. The strip is fixed-width, so the count shortens that tab's label and moves nothing beside it.

4. The star on the tab, and wider tabs

A starred chat's tab draws a filled star before its label, in the tab's own ink (no new colour), in the strip and in the "N more" menu. FIXED_TAB_WIDTH goes from w-40 (160px) to w-48 (192px) so the star, the unread count and the × fit beside the label.

Not in this PR

abilityai/trinity-enterprise#842 (a star in the composer row) is not implemented: the operator decided to keep the existing header star and add no new control.

Tests

  • tests/unit/test_ent841_close_chat.py — 8 passed. Wider portal/workspace backend unit set: 876 passed.
  • Frontend unit (vitest run): 293 files, 5007 tests passed. New: portalStarRace, portalChatTabsClose, overflowTabsClosable.mount.
  • E2E @smoke: 103 passed, 7 skipped. New hermetic specs:
    • workspace-star-first-click.spec.js holds a /chat-state read open across the click. All 3 cases fail on the base commit and pass here.
    • workspace-close-chat.spec.js covers close → neighbour → Archived → Undo, reopen from Archived, and the tab unread count with equal tab widths.
  • Light and dark screenshots of the strip, the "N more" menu and the toast checked by eye.

Not run: the new archive routes against a live backend. The local dev stack serves a different checkout, so they are covered by the service-level unit tests and the hermetic e2e fake.

The raw-colour baseline is re-frozen in its own commit: OverflowTabs.vue +2 gray for the ×'s hover ground.

Docs

requirements §5.41 (new) and the superseded line in §5.23, architecture/workspace.md, two feature flows, tests/registry.json.

Fixes #3356
Fixes abilityai/trinity-enterprise#841

🤖 Generated with Claude Code

vybe and others added 3 commits October 8, 2026 10:34
… a star that sticks on the first click (Abilityai/trinity-enterprise#841, #3356)

#3356 — the first star click looked lost because a chat-state read that was
already in flight (the 20 s poll, a turn finishing) answered with the pre-click
state and `refreshThreads` overwrote the optimistic star. The server had the
star; the screen did not; the second click re-sent the same PUT after the stale
read had gone by. Local star writes are now held over any read issued before
they settled (`createWriteGuard`). A starred, never-used Main is also listed in
the sidebar, so the header star on a fresh agent has a Starred row to show.

ent#841 — every chat tab but Main gets an × that ARCHIVES the chat (never
deletes): PUT/DELETE .../sessions/{id}/archive, one click, an Undo toast, an
InlineError under the strip on refusal. One archive rule (operator, 2026-10-08):
any archived chat, Reset's retired Main included, leaves the tab strip and the
sidebar's active groups and is listed under a collapsed Archived group; opening
it there reopens it. This reverses the ent#523 "becomes the newest tab" ruling.
The open chat's × is inert while its reply is in flight. Rooms are excluded:
the strip lists one agent's 1:1 chats, so a room has no tab. No schema change.

Also: a background chat with unread replies shows the sidebar row's blue count
on its tab (and in the "N more" menu); the strip is fixed-width, so nothing moves.

Fixes #3356

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-button hover ground (+2 gray, Abilityai/trinity-enterprise#841)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…widen to 192px (Abilityai/trinity-enterprise#841)

OverflowTabs gains an opt-in `starred` glyph (visible row, menu and mirror, in
the tab's own ink — no new colour). FIXED_TAB_WIDTH goes w-40 -> w-48 so the
star, the unread count and the close × fit beside the label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vybe
vybe force-pushed the feature/3356-star-close-chat branch from 6518ab2 to 450d727 Compare October 8, 2026 09:40
…-train, #3358)

Every close shows the same words, so the toast's timer, restarted only by a
watch on the message text, never restarted for a second close: chat B's Undo
disappeared on chat A's timer. The restart is now keyed on a per-close
sequence number set where closeChat assigns undoClose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@trinity-ability

Copy link
Copy Markdown
Contributor Author

merge-train: one mechanical fix pushed, 7efc41d47.

A second close now restarts the Undo toast's clock. Portal.vue passed the same 'Chat archived' string for every close and PortalUndoToast.vue restarted its 8s timer only on a change of that text, so closing chat B while A's toast was showing left B's Undo to disappear on A's timer. The restart is now keyed on a per-close sequence number (closeKey), not the text. A counter rather than the chat id, because the same chat can be closed twice under one toast.

New spec portalUndoToastClock.mount.spec.js (jsdom mount, fake timers): 1 failed / 2 passed on the previous head, 3 passed now. Full frontend unit suite 5010 passed; raw-color, loading-gate and source-text ratchets pass with no baseline touched.

Body: added Fixes #3356 and Fixes abilityai/trinity-enterprise#841 as standalone lines.

Approved at the batch gate by the operator: the two-class gray growth on OverflowTabs.vue in the raw-color baseline, and archived chats leaving the tab strip (the ent#523 reversal).

Left as is, for follow-up: unread on an archived chat has no visible mark on the collapsed Archived group; no HTTP-level test of the two archive routes, the refused-close path or the busy-inert close button; the toast is a new component beside useNotification.

…son rebuilt, all entries kept

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge-train: batch validated on train PR #3409. The Undo toast clock fix is in; the baseline growth on OverflowTabs.vue and archived chats leaving the tab strip were approved at the batch gate.

@vybe
vybe merged commit a18b024 into dev Oct 8, 2026
25 checks 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.

2 participants