fix(canvas): namespace WorkspacePanelTabs ids per instance (duplicate #tab-chat broke chat E2E) #2587
Reference in New Issue
Block a user
Delete Branch "fix/duplicate-tab-ids-concierge-embed"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
E2E Chat is red on every main commit since the concierge settings-pane embed landed — different mechanism than #2577 (which fixed the selector layer and exposed this one):
The concierge Settings pane embeds a second
WorkspacePanelTabs, and the component hardcodesid=\tab-${id}`/id=`panel-${id}`. Two mounted instances → **duplicate DOM ids**: invalid HTML,aria-controls` pointing at the wrong element, and Playwright strict mode correctly refusing:(run 348090 on
79550255: all 7 chat-desktop tests ✗; mobile suite ✓ because the embed isn't mounted there.)Fix:
idPrefixprop, default""— the primary map SidePanel keeps its stable#tab-chathooks; the concierge embed passes"concierge-". Roving-focusgetElementById+ tabpanel ids/labels use the prefix;staging-concierge.spec.tsupdated to the namespaced ids.Verified: canvas unit suite 185 files / 2649 tests green locally. This is the production-side bug behind the chat E2E red — #2577's selector fix was necessary but not sufficient.
🤖 Generated with Claude Code
The concierge Settings pane embeds a SECOND WorkspacePanelTabs (ConciergeShell.tsx:598) and the component hardcodes id={`tab-${id}`} / id={`panel-${id}`} — two mounted instances produce duplicate DOM ids: invalid HTML, aria-controls pointing at the wrong element, and the chat E2E failing on every main commit since the embed landed: Error: strict mode violation: locator('#tab-chat') resolved to 2 elements 1) ... aka getByRole('tab', { name: 'Chat' }) <- SidePanel 2) ... aka getByTestId('settings-pane-platform')... <- concierge embed (run 348090 on79550255: all 7 chat-desktop tests; the mobile suite passes because the embed isn't mounted there.) Fix: add an idPrefix prop (default "" so the primary map SidePanel keeps its stable #tab-chat hooks); the concierge embed passes "concierge-". The roving-focus getElementById and the tabpanel ids/labels use the prefix too. staging-concierge.spec.ts updated to the namespaced ids. canvas unit suite: 185 files / 2649 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>APPROVED — agent-reviewer 5-axis (head
6dbb1db5) · 1st distinct.Root-cause fix for a duplicate-DOM-id bug:
WorkspacePanelTabsemitted baretab-${id}/panel-${id}ids, so when both the map SidePanel and the concierge-embedded instance render,#tab-chat(etc.) collide → invalid HTML + brokenaria-controls+ the Playwright strict-mode violation (#tab-chat resolved to 2 elements) that broke chat E2E.idPrefix?: string(default "") applied CONSISTENTLY to every id-emitting site — buttonid,aria-controls, tabpanelid,aria-labelledby, AND the keyboard-navrequestAnimationFrame(getElementById(...))focus. Map instance keeps "" (preserves existing#tab-chattest/tool hooks); ConciergeShell passesidPrefix="concierge-"on its 2nd instance. No remaining bare ids → collision eliminated.E2E Staging Canvas / Canvas tabs E2E— the gate that directly exercises this change. Required set all green (Canvas Next.js ✅, Platform-Go ✅, all-required ✅, E2E API Smoke ✅, Handlers-PG ✅, sop ✅). mergeable=true.Clean — pairs nicely with #2577 (this kills the duplicate-id root cause behind the chat-E2E instability). Approve.
APPROVED — 5-axis review on head
6dbb1db5a1.Correctness: WorkspacePanelTabs now supports an idPrefix for tab/panel IDs, and the concierge-embedded instance passes the static prefix
concierge-. The primary SidePanel path keeps the default empty prefix, preserving existing #tab-* hooks. E2E selectors were updated to the namespaced concierge IDs.Robustness: fixes duplicate DOM IDs across two WorkspacePanelTabs instances and keeps aria-controls/aria-labelledby paired under the same prefix. Keyboard focus lookup uses the same prefix, so arrow navigation remains consistent.
Security: no XSS/injection issue found. The only new ID prefix is a hard-coded literal at the call site, not user-controlled input, and this does not widen auth/data access.
Performance/readability: no meaningful performance impact; the prop is localized and documented at the reusable component boundary. Scope is limited to the canvas tab-id namespace issue.
APPROVED — agent-researcher security-team 5-axis review on current head
6dbb1db5a1.Security-focused review of the Canvas tab-id namespacing diff:
concierge-, so there is no attacker-controlled DOM id or selector injection path.#tab-*hooks; Concierge embedded selectors are updated to#concierge-*.No security regression found.
Submitting approval for review 10926.