Skip to content

feat(sidebar-tag-collapse-and-delete): collapsible tag area + guarded global tag delete - #387

Merged
robertcsakany merged 3 commits into
developfrom
os/sidebar-tag-collapse-and-delete
Jul 22, 2026
Merged

feat(sidebar-tag-collapse-and-delete): collapsible tag area + guarded global tag delete#387
robertcsakany merged 3 commits into
developfrom
os/sidebar-tag-collapse-and-delete

Conversation

@robertcsakany

Copy link
Copy Markdown
Member

Implements OpenSpec change sidebar-tag-collapse-and-delete.

What

  • Master collapse over the entire sidebar tag+phase area — default-collapsed, persisted (localStorage). Collapsed header shows N tags · M phases, an active-selection indicator when a filter is active, and a fold-free clear affordance (D8). Phases stay a distinct read-only sub-group (D9).
  • Overflow cap (10) + +N more/show less inline expander on the user-tag group.
  • Guarded destructive per-tag delete (✕) on user filter chips → confirm dialog (names tag, cross-folder carrying count, not-undoable + reappears-if-re-added) → dispatches the new remove_tag_globally { tag } browser→server verb. Server normalizes, fans out over listAll(), strips from every carrier via the existing normalize→update→broadcast path (one session_updated per changed session). Phase chips stay read-only.

Tests

  • L1 (vitest): handleRemoveTagGlobally — E3–E6, X3 (fan-out, no-op, blank, normalize, reconnect replay).
  • Component (vitest/RTL): E1/E2 overflow cap, F2 remove≠toggle, F4 phase-read-only, X2 keyboard ✕.
  • L3 (Playwright vs docker harness): E7/E8 default-collapsed+persist, F3 collapsed active indicator, X1 cancel+confirm round-trip, F1 cross-context convergence — all green.

QA/manual tasks (9.1 derived-union reappear, X4 concurrent-write race) deferred to post-merge verification.

… global tag delete

Implements OpenSpec change sidebar-tag-collapse-and-delete:
- Single master collapse over the sidebar tag+phase area (default collapsed,
  persisted; count + active-filter indicator + fold-free clear on the header).
- Overflow cap (10) + `+N more`/`show less` inline expander on the user-tag group.
- Destructive per-tag remove (guarded confirm dialog) that strips a tag from
  every carrying session via the new `remove_tag_globally` browser->server verb
  (server fan-out; one session_updated per changed session).

Archives + syncs specs. QA/manual tasks (9.1, X4) deferred to post-merge.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@robertcsakany, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1072a8e2-3e54-4e56-8f70-123c97d2a8f9

📥 Commits

Reviewing files that changed from the base of the PR and between 9fefef7 and 2303225.

📒 Files selected for processing (5)
  • packages/client/src/components/session/SessionList.tsx
  • packages/server/src/pairing/browser-gateway.ts
  • packages/server/src/pairing/browser-gateway.ts.AGENTS.md
  • packages/shared/src/AGENTS.md
  • packages/shared/src/browser-protocol.ts

Walkthrough

The sidebar tag area now collapses as one persisted section, caps visible user tags with an overflow control, and supports confirmed global removal of user tags. A new browser protocol message, server fan-out handler, client action, UI components, specifications, mockup, and tests implement the behavior.

Changes

Sidebar Tags

Layer / File(s) Summary
Behavior contracts and validation plan
openspec/changes/archive/..., openspec/specs/session-tags/spec.md
Specifications and task documents define persisted collapse, overflow expansion, global removal, accessibility behavior, rollout, and verification scenarios.
UX mockup and interaction flow
openspec/changes/archive/.../mockups/*
The mockup demonstrates the collapsed tag area, overflow control, confirmation dialog, persistence, themes, and read-only phase chips.
Global removal protocol and server fan-out
packages/shared/src/browser-protocol.ts, packages/server/src/browser-handlers/..., packages/server/src/pairing/browser-gateway.ts
A remove_tag_globally message is routed to a normalized handler that updates and broadcasts each matching session.
Client sidebar and tag controls
packages/client/src/components/..., packages/client/src/hooks/..., packages/client/src/lib/session/..., packages/client/src/App.tsx
The client adds persisted collapse state, active-filter clearing, capped tag groups, independent delete controls, confirmation UI, and global-removal dispatch.
Component and end-to-end validation
packages/client/src/components/__tests__/*, packages/client/src/components/tags/__tests__/*, packages/server/src/browser-handlers/__tests__/*, tests/e2e/session-tags.spec.ts
Tests cover collapse persistence, overflow, chip controls, confirmation behavior, normalized server removal, broadcasts, and cross-context updates.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SessionList
  participant BrowserGateway
  participant SessionMetaHandler
  participant SessionManager
  User->>SessionList: confirm tag removal
  SessionList->>BrowserGateway: send remove_tag_globally
  BrowserGateway->>SessionMetaHandler: route message
  SessionMetaHandler->>SessionManager: update matching sessions
  SessionManager-->>User: broadcast session_updated events
Loading

Possibly related PRs

Poem

A bunny folds the sidebar tight,
Then fans ten tags into the light.
A tiny ✕ asks, “Are you sure?”
The server hops through sessions pure.
Across two screens, updates spring!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: a collapsible tag area with guarded global tag deletion.
Description check ✅ Passed The description is directly related and accurately describes the tag collapse and global delete changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch os/sidebar-tag-collapse-and-delete

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@robertcsakany

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (1)
packages/client/src/components/session/SessionList.tsx (1)

1361-1382: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded count/badge strings bypass t().

The count ({allTags.length} tag{…}s · {phasesInUse.length} phase{…}s) and the {activeFilterCount} active badge are literal English, while the surrounding component routes all user-facing copy through t(). Consider localizing these for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/client/src/components/session/SessionList.tsx` around lines 1361 -
1382, Localize the tag/phase count text and active-filter badge in the tag
filter bar by routing their user-facing strings through the component’s existing
t() helper. Preserve the current pluralization and interpolated counts, and
update the “active” badge text without changing its display conditions or
styling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml`:
- Around line 1-2: Move the entire change directory from
openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/ to
openspec/changes/2026-07-22-sidebar-tag-collapse-and-delete/, preserving
.openspec.yaml, design.md, proposal.md, specs/session-tags/spec.md, tasks.md,
test-plan.md, and the mockups subtree. Update mockups/ux-review.md to reference
the relocated mockup, with no content changes required elsewhere.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html`:
- Around line 61-66: Update the remove control styling around .chip .rm and the
toggle styling around the referenced toggle rule to meet the documented
accessibility floor: ensure interactive targets are at least 24px and preserve a
clearly visible focus indicator instead of removing the native outline with all:
unset. Keep the existing visual states and behavior unchanged.
- Around line 102-118: Update the tag-filter mockup around the area header and
chip rendering logic to model active selections: maintain selection state,
toggle each chip’s aria-pressed value, and reflect selected filters in the
header counts. Add a folded-state clear-filters control that appears when
filters are active and clears all selected tags and phases, including the
corresponding count and accessibility updates.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md`:
- Around line 17-18: Align the test-plan entries E1, E2, F2, F4, and X2 with
their implemented component-test level instead of L3, and update the L3 coverage
summary to match. Preserve the declared Playwright coverage definition; only add
Playwright tests if retaining those rows as L3.

In `@packages/client/src/components/session/SessionList.tsx`:
- Around line 1433-1439: Update the TagDeleteConfirmDialog onConfirm handler to
remove pendingDeleteTag from selectedTags before or alongside calling
onRemoveTagGlobally, while preserving other selected tags and the existing
global removal behavior. Use the selectedTags state update mechanism and ensure
the confirmed tag is no longer passed to passesTagAxes after deletion.

In `@packages/client/src/components/tags/AGENTS.md`:
- Line 10: Reorder the file table entry for TagDeleteConfirmDialog.tsx so it
appears before TagEditor.tsx, maintaining strict path-alphabetical ordering
while leaving the entry content unchanged.

In `@packages/server/src/browser-handlers/session-meta-handler.ts`:
- Around line 91-95: Update the global tag-removal loop in the session metadata
handler to perform each session’s tag removal as an atomic, serialized
read-modify-write operation. Re-read tags within the per-session mutation or use
the session manager’s compare-and-swap/retry mechanism, then remove only the
target tag while preserving concurrent updates and making repeated global
deletions safe.
- Around line 89-90: Validate msg.tag with a runtime typeof check ensuring it is
a string before calling normalizeTags in the session metadata handler; for null
or other invalid payload values, return the handler’s established structured
error or no-op path. Preserve normalization for valid string tags and prevent
malformed input from reaching normalizeTags.

---

Nitpick comments:
In `@packages/client/src/components/session/SessionList.tsx`:
- Around line 1361-1382: Localize the tag/phase count text and active-filter
badge in the tag filter bar by routing their user-facing strings through the
component’s existing t() helper. Preserve the current pluralization and
interpolated counts, and update the “active” badge text without changing its
display conditions or styling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e568658d-de69-4a78-8243-ab4817bc64f1

📥 Commits

Reviewing files that changed from the base of the PR and between e8eec9c and e2ec722.

📒 Files selected for processing (31)
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/design.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/ux-review.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/proposal.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/specs/session-tags/spec.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/tasks.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md
  • openspec/specs/session-tags/spec.md
  • packages/client/src/App.tsx
  • packages/client/src/components/__tests__/SessionList.seek-to-card.test.tsx
  • packages/client/src/components/__tests__/SessionList.tags-filter.test.tsx
  • packages/client/src/components/session/SessionList.tsx
  • packages/client/src/components/session/SessionList.tsx.AGENTS.md
  • packages/client/src/components/tags/AGENTS.md
  • packages/client/src/components/tags/TagChip.tsx
  • packages/client/src/components/tags/TagDeleteConfirmDialog.tsx
  • packages/client/src/components/tags/TagFilterGroup.tsx
  • packages/client/src/components/tags/__tests__/tags-components.test.tsx
  • packages/client/src/hooks/AGENTS.md
  • packages/client/src/hooks/useSessionActions.ts
  • packages/client/src/lib/session/session-filter-storage.ts
  • packages/client/src/lib/session/session-filter-storage.ts.AGENTS.md
  • packages/server/src/browser-handlers/AGENTS.md
  • packages/server/src/browser-handlers/__tests__/session-meta-handler.test.ts
  • packages/server/src/browser-handlers/session-meta-handler.ts
  • packages/server/src/pairing/browser-gateway.ts
  • packages/server/src/pairing/browser-gateway.ts.AGENTS.md
  • packages/shared/src/AGENTS.md
  • packages/shared/src/browser-protocol.ts
  • tests/e2e/session-tags.spec.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 8

🧹 Nitpick comments (1)
packages/client/src/components/session/SessionList.tsx (1)

1361-1382: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded count/badge strings bypass t().

The count ({allTags.length} tag{…}s · {phasesInUse.length} phase{…}s) and the {activeFilterCount} active badge are literal English, while the surrounding component routes all user-facing copy through t(). Consider localizing these for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/client/src/components/session/SessionList.tsx` around lines 1361 -
1382, Localize the tag/phase count text and active-filter badge in the tag
filter bar by routing their user-facing strings through the component’s existing
t() helper. Preserve the current pluralization and interpolated counts, and
update the “active” badge text without changing its display conditions or
styling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml`:
- Around line 1-2: Move the entire change directory from
openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/ to
openspec/changes/2026-07-22-sidebar-tag-collapse-and-delete/, preserving
.openspec.yaml, design.md, proposal.md, specs/session-tags/spec.md, tasks.md,
test-plan.md, and the mockups subtree. Update mockups/ux-review.md to reference
the relocated mockup, with no content changes required elsewhere.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html`:
- Around line 61-66: Update the remove control styling around .chip .rm and the
toggle styling around the referenced toggle rule to meet the documented
accessibility floor: ensure interactive targets are at least 24px and preserve a
clearly visible focus indicator instead of removing the native outline with all:
unset. Keep the existing visual states and behavior unchanged.
- Around line 102-118: Update the tag-filter mockup around the area header and
chip rendering logic to model active selections: maintain selection state,
toggle each chip’s aria-pressed value, and reflect selected filters in the
header counts. Add a folded-state clear-filters control that appears when
filters are active and clears all selected tags and phases, including the
corresponding count and accessibility updates.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md`:
- Around line 17-18: Align the test-plan entries E1, E2, F2, F4, and X2 with
their implemented component-test level instead of L3, and update the L3 coverage
summary to match. Preserve the declared Playwright coverage definition; only add
Playwright tests if retaining those rows as L3.

In `@packages/client/src/components/session/SessionList.tsx`:
- Around line 1433-1439: Update the TagDeleteConfirmDialog onConfirm handler to
remove pendingDeleteTag from selectedTags before or alongside calling
onRemoveTagGlobally, while preserving other selected tags and the existing
global removal behavior. Use the selectedTags state update mechanism and ensure
the confirmed tag is no longer passed to passesTagAxes after deletion.

In `@packages/client/src/components/tags/AGENTS.md`:
- Line 10: Reorder the file table entry for TagDeleteConfirmDialog.tsx so it
appears before TagEditor.tsx, maintaining strict path-alphabetical ordering
while leaving the entry content unchanged.

In `@packages/server/src/browser-handlers/session-meta-handler.ts`:
- Around line 91-95: Update the global tag-removal loop in the session metadata
handler to perform each session’s tag removal as an atomic, serialized
read-modify-write operation. Re-read tags within the per-session mutation or use
the session manager’s compare-and-swap/retry mechanism, then remove only the
target tag while preserving concurrent updates and making repeated global
deletions safe.
- Around line 89-90: Validate msg.tag with a runtime typeof check ensuring it is
a string before calling normalizeTags in the session metadata handler; for null
or other invalid payload values, return the handler’s established structured
error or no-op path. Preserve normalization for valid string tags and prevent
malformed input from reaching normalizeTags.

---

Nitpick comments:
In `@packages/client/src/components/session/SessionList.tsx`:
- Around line 1361-1382: Localize the tag/phase count text and active-filter
badge in the tag filter bar by routing their user-facing strings through the
component’s existing t() helper. Preserve the current pluralization and
interpolated counts, and update the “active” badge text without changing its
display conditions or styling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e568658d-de69-4a78-8243-ab4817bc64f1

📥 Commits

Reviewing files that changed from the base of the PR and between e8eec9c and e2ec722.

📒 Files selected for processing (31)
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/design.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/ux-review.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/proposal.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/specs/session-tags/spec.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/tasks.md
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md
  • openspec/specs/session-tags/spec.md
  • packages/client/src/App.tsx
  • packages/client/src/components/__tests__/SessionList.seek-to-card.test.tsx
  • packages/client/src/components/__tests__/SessionList.tags-filter.test.tsx
  • packages/client/src/components/session/SessionList.tsx
  • packages/client/src/components/session/SessionList.tsx.AGENTS.md
  • packages/client/src/components/tags/AGENTS.md
  • packages/client/src/components/tags/TagChip.tsx
  • packages/client/src/components/tags/TagDeleteConfirmDialog.tsx
  • packages/client/src/components/tags/TagFilterGroup.tsx
  • packages/client/src/components/tags/__tests__/tags-components.test.tsx
  • packages/client/src/hooks/AGENTS.md
  • packages/client/src/hooks/useSessionActions.ts
  • packages/client/src/lib/session/session-filter-storage.ts
  • packages/client/src/lib/session/session-filter-storage.ts.AGENTS.md
  • packages/server/src/browser-handlers/AGENTS.md
  • packages/server/src/browser-handlers/__tests__/session-meta-handler.test.ts
  • packages/server/src/browser-handlers/session-meta-handler.ts
  • packages/server/src/pairing/browser-gateway.ts
  • packages/server/src/pairing/browser-gateway.ts.AGENTS.md
  • packages/shared/src/AGENTS.md
  • packages/shared/src/browser-protocol.ts
  • tests/e2e/session-tags.spec.ts
🛑 Comments failed to post (8)
openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml (1)

1-2: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Move this OpenSpec change out of archive/.

These artifacts are being introduced under a forbidden location, preventing the change from following the repository’s required OpenSpec layout.

  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml#L1-L2: move metadata with the change directory.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/design.md#L1-L60: move to the non-archive change directory.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/proposal.md#L1-L35: move to the non-archive change directory.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/specs/session-tags/spec.md#L1-L117: preserve the delta spec under the relocated change directory.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/tasks.md#L1-L66: move with the change artifacts.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md#L1-L55: move with the change artifacts.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html#L1-L252: move with the mockup subtree.
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/ux-review.md#L1-L46: move with the mockup subtree and update its mockup reference.

As per coding guidelines, openspec/changes/**/* artifacts must be stored under openspec/changes/<name>/, never under active/ or archive/.

📍 Affects 8 files
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml#L1-L2 (this comment)
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/design.md#L1-L60
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/proposal.md#L1-L35
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/specs/session-tags/spec.md#L1-L117
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/tasks.md#L1-L66
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md#L1-L55
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html#L1-L252
  • openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/ux-review.md#L1-L46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/.openspec.yaml`
around lines 1 - 2, Move the entire change directory from
openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/ to
openspec/changes/2026-07-22-sidebar-tag-collapse-and-delete/, preserving
.openspec.yaml, design.md, proposal.md, specs/session-tags/spec.md, tasks.md,
test-plan.md, and the mockups subtree. Update mockups/ux-review.md to reference
the relocated mockup, with no content changes required elsewhere.

Source: Coding guidelines

openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html (2)

61-66: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Meet the documented accessibility floor in the mockup.

Line 62 limits the remove target to 16px, and Line 194 removes the toggle’s native focus outline with all: unset. This contradicts the stated ≥24px target and visible-focus checks.

Proposed adjustment
-  .chip .rm{margin-left:1px;border:none;background:none;cursor:pointer;color:currentColor;
-    opacity:.45;font-size:11px;line-height:1;padding:0 3px;border-radius:3px;min-width:16px}
+  .chip .toggle:focus-visible,
+  .chip .rm:focus-visible{outline:2px solid currentColor;outline-offset:2px}
+  .chip .rm{margin-left:1px;border:none;background:none;cursor:pointer;color:currentColor;
+    opacity:.45;font-size:11px;line-height:1;padding:0;border-radius:3px;
+    min-width:24px;min-height:24px;display:inline-flex;align-items:center;justify-content:center}
-      chip.innerHTML=`<button style="all:unset;cursor:pointer" aria-pressed="false" aria-label="Filter by tag ${name}">#${name}</button>`;
+      chip.innerHTML=`<button class="toggle" aria-pressed="false" aria-label="Filter by tag ${name}">#${name}</button>`;

Also applies to: 192-198

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html`
around lines 61 - 66, Update the remove control styling around .chip .rm and the
toggle styling around the referenced toggle rule to meet the documented
accessibility floor: ensure interactive targets are at least 24px and preserve a
clearly visible focus indicator instead of removing the native outline with all:
unset. Keep the existing visual states and behavior unchanged.

102-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Model the required folded active-filter behavior.

The mockup has no selection state: chip toggles remain aria-pressed="false", the header only renders counts, and no folded clear-filters control exists. It therefore cannot validate the active-selection requirement.

Also applies to: 192-205, 219-222

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/mockups/index.html`
around lines 102 - 118, Update the tag-filter mockup around the area header and
chip rendering logic to model active selections: maintain selection state,
toggle each chip’s aria-pressed value, and reflect selected filters in the
header counts. Add a folded-state clear-filters control that appears when
filters are active and clears all selected tags and phases, including the
corresponding count and accessibility updates.
openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md (1)

17-18: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the declared test level with the implemented tests.

This plan defines L3 as Playwright, but tasks.md records E1, E2, F2, F4, and X2 as component tests. Correct these rows and the L3 coverage summary, or add the corresponding Playwright coverage.

Also applies to: 31-33, 40-40

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@openspec/changes/archive/2026-07-22-sidebar-tag-collapse-and-delete/test-plan.md`
around lines 17 - 18, Align the test-plan entries E1, E2, F2, F4, and X2 with
their implemented component-test level instead of L3, and update the L3 coverage
summary to match. Preserve the declared Playwright coverage definition; only add
Playwright tests if retaining those rows as L3.
packages/client/src/components/session/SessionList.tsx (1)

1433-1439: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear the deleted tag from selectedTags on confirm.

When the confirmed tag is currently an active filter (selectedTags holds it), onRemoveTagGlobally strips it from every session, allTags recomputes without it, but selectedTags still contains it. passesTagAxes then hides every session, and if that tag was the only entry the whole tag-filter-bar (allTags.length > 0 || phasesInUse.length > 0) unmounts — taking the clear affordance with it and stranding the user until reload.

🐛 Proposed fix: drop the tag from the selection when deleting
             {pendingDeleteTag != null && onRemoveTagGlobally && (
               <TagDeleteConfirmDialog
                 tag={pendingDeleteTag}
                 count={deleteTagCount}
-                onConfirm={() => onRemoveTagGlobally(pendingDeleteTag)}
+                onConfirm={() => {
+                  onRemoveTagGlobally(pendingDeleteTag);
+                  setSelectedTags((prev) => {
+                    if (!prev.has(pendingDeleteTag)) return prev;
+                    const next = new Set(prev);
+                    next.delete(pendingDeleteTag);
+                    return next;
+                  });
+                }}
                 onClose={() => setPendingDeleteTag(null)}
               />
             )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            {pendingDeleteTag != null && onRemoveTagGlobally && (
              <TagDeleteConfirmDialog
                tag={pendingDeleteTag}
                count={deleteTagCount}
                onConfirm={() => {
                  onRemoveTagGlobally(pendingDeleteTag);
                  setSelectedTags((prev) => {
                    if (!prev.has(pendingDeleteTag)) return prev;
                    const next = new Set(prev);
                    next.delete(pendingDeleteTag);
                    return next;
                  });
                }}
                onClose={() => setPendingDeleteTag(null)}
              />
            )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/client/src/components/session/SessionList.tsx` around lines 1433 -
1439, Update the TagDeleteConfirmDialog onConfirm handler to remove
pendingDeleteTag from selectedTags before or alongside calling
onRemoveTagGlobally, while preserving other selected tags and the existing
global removal behavior. Use the selectedTags state update mechanism and ensure
the confirmed tag is no longer passed to passesTagAxes after deletion.
packages/client/src/components/tags/AGENTS.md (1)

10-10: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep the AGENTS.md file table path-alphabetical.

Move TagDeleteConfirmDialog.tsx before TagEditor.tsx; the new row is currently inserted after TagFilterGroup.tsx.

As per coding guidelines: maintain per-file purpose records in the nearest AGENTS.md and keep rows path-alphabetical.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/client/src/components/tags/AGENTS.md` at line 10, Reorder the file
table entry for TagDeleteConfirmDialog.tsx so it appears before TagEditor.tsx,
maintaining strict path-alphabetical ordering while leaving the entry content
unchanged.

Source: Coding guidelines

packages/server/src/browser-handlers/session-meta-handler.ts (2)

89-90: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Validate the wire payload before normalizing it.

BrowserToServerMessage is only a TypeScript cast after JSON.parse; a malformed client can send tag: null or another non-string value. normalizeTags([msg.tag]) then calls .trim() and throws, so the request is dropped by the gateway error boundary instead of being handled cleanly. Add a runtime typeof msg.tag === "string" check and return a structured error or no-op.

As per coding guidelines: validate all agent inputs before processing and handle errors gracefully with meaningful error messages.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/browser-handlers/session-meta-handler.ts` around lines 89
- 90, Validate msg.tag with a runtime typeof check ensuring it is a string
before calling normalizeTags in the session metadata handler; for null or other
invalid payload values, return the handler’s established structured error or
no-op path. Preserve normalization for valid string tags and prevent malformed
input from reaching normalizeTags.

Source: Coding guidelines


91-95: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make global tag removal an atomic read-modify-write.

The handler snapshots session.tags from listAll() and later writes a replacement array. A concurrent set_session_tags or second global deletion can update the same session between those operations, allowing this stale write to overwrite newer tags. Use a per-session serialized mutation or compare-and-swap/retry mechanism.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/browser-handlers/session-meta-handler.ts` around lines 91
- 95, Update the global tag-removal loop in the session metadata handler to
perform each session’s tag removal as an atomic, serialized read-modify-write
operation. Re-read tags within the per-session mutation or use the session
manager’s compare-and-swap/retry mechanism, then remove only the target tag
while preserving concurrent updates and making repeated global deletions safe.

- Localize tag-area count + active badge via t() (#1)
- Deselect a globally-deleted tag from the active filter set on confirm so
  the list can't stay filtered to 0 with no chip to clear (#5)
- Path-alphabetical order for the TagDeleteConfirmDialog AGENTS.md row (#6)
- Guard non-string `tag` on remove_tag_globally before normalize (untrusted
  WS payload; normalizeTags calls .trim()) + L1 test (#8)

Rejected (with reason): #2 archive dir is the project ship convention;
#3 archived static mockups (real components already meet 24px+focus/active-
selection); #4 test-plan is the frozen plan-stage artifact, level deviation
documented in tasks.md; #7 last-write-wins fan-out is the design's documented
accepted trade-off (D5/Risks, scenario X4 manual-only).
@robertcsakany

Copy link
Copy Markdown
Member Author

Thanks — addressed the review. Applied #1 (localize count/badge via t()), #5 (deselect a globally-deleted tag from the active filter on confirm), #6 (path-alphabetical AGENTS.md row), #8 (guard non-string tag before normalizeTags + L1 test) in 9fefef7.

Intentionally not applied:

  • Tui dashboard bridge rework #2 (move dir out of archive/): archiving to openspec/changes/archive/YYYY-MM-DD-<change>/ is this repo's ship convention.
  • Some issues #3 (mockup a11y/active-selection): those are frozen static design mockups under the archived change; the shipped components already meet the 24px hit-area + focus ring (TagChip) and active-selection indicator (SessionList, D8).
  • Unknown publisher - windows 11 #4 (test-plan level): test-plan.md is the frozen plan-stage artifact; the E1/E2/F2/F4/X2 → component-test realization is documented in tasks.md.
  • fix settings tab pointer cursor #7 (atomic/CAS fan-out): the last-write-wins race is a documented, accepted trade-off (design D5 / Risks / scenario X4, manual-only) for a non-critical UI tag; re-issuing the delete is idempotent.

…lapse-and-delete

# Conflicts:
#	packages/client/src/components/session/SessionList.tsx
#	packages/server/src/pairing/browser-gateway.ts.AGENTS.md
@robertcsakany
robertcsakany merged commit 612d761 into develop Jul 22, 2026
2 checks passed
@robertcsakany
robertcsakany deleted the os/sidebar-tag-collapse-and-delete branch July 22, 2026 12:38
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