Skip to content

feat(agent): agent opt-in consent card (design prototype) - #15661

Draft
uytieu wants to merge 13 commits into
nathaniel/agent-panel-v1from
uy/agent-consent-card
Draft

feat(agent): agent opt-in consent card (design prototype)#15661
uytieu wants to merge 13 commits into
nathaniel/agent-panel-v1from
uy/agent-consent-card

Conversation

@uytieu

@uytieu uytieu commented Aug 23, 2026

Copy link
Copy Markdown

Summary

Clicking Ask Comfy Agent now shows a consent card before the agent panel opens for the first time, so users agree to agent access to their workflow before it starts.

Ticket: DES-810. This PR adds a new feature. It does not fix an issue.

Important

This is a design PR, not production code. It is a working prototype that shows how the design looks and how it can behave. Review it to decide which parts to keep and which parts to rewrite. Treat every choice in it as a proposal, including the storage, the copy, the video, and the wiring.

Changes

image
  • What: A new AgentConsentCard component and a useAgentConsent composable. The card shows a title, two paragraphs, a video, and three actions: Read the docs, Reject, and Accept. The composable stores the answer and puts the card up when there is no answer yet. WorkflowTabs calls it before it opens the panel. Accept opens the panel. Reject leaves it closed. Later clicks open the panel directly. The card has Storybook stories and unit tests.
  • Breaking: None. agentPanelStore.open is now exported, which is additive. Cloud users see one new dialog on first use.
  • Dependencies: None.

Review Focus

The copy is not final. The Figma frames contain lorem ipsum, so the text in agent.consent.* is a first draft and needs a review.

The video is a stand-in. It is the MCP launch film, because there is no consent video yet.

The answer is stored in the browser, under the Comfy.AgentPanel.consentAccepted key in localStorage, keyed by user id. Two accounts on one browser profile each get their own answer. The answer does not follow a user to another browser or machine. A server-side record is the better long-term home.

A user cannot withdraw consent after Accept. There is no setting for this yet. The copy makes no promise about withdrawal, because the feature does not exist.

Escape declines the card. The mask stays inert, so a stray click cannot dismiss it. Escape is also the only way out if the card ever fails to render, which happened once during development.

The card sizes itself with container queries, not viewport breakpoints. It stacks below a 672px container, so it also stacks inside a narrow agent panel on a wide screen.

?agentConsent=always shows the card on every open and records nothing. It exists for demos. Decide whether it ships.

The agent panel is cloud-only and tree-shaken out of other builds. The card loads through defineAsyncComponent to keep it out of those bundles. Please make sure that this holds after any import changes.

Screenshots (if applicable)

The Agent/ConsentCard stories in Storybook show the card: Default, Without Video, Single Paragraph, Long Copy, and In Narrow Panel for the stacked layout. CI deploys a Storybook preview for this PR.

image
Screen.Recording.2026-08-22.at.6.22.08.PM.mov

uytieu and others added 12 commits August 22, 2026 13:07
Adds the agent opt-in consent dialog from DES-810: a two-column card with
a looping video on the left and the consent copy plus Read the docs,
Reject and Accept actions on the right.

Title and body are props so the component carries no fixed copy; only the
action labels are translated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds Storybook coverage for the consent card so it can be reviewed without
being wired into the app: default, no-video, single-paragraph and long-copy
states, defaulting the theme toolbar to dark.

Buttons now set border-0 and an explicit background. Storybook does not apply
the preflight button reset, so the user-agent 2px outset border was showing
through on every action.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The card stacks to a single column below a 672px container: the video takes
a 16:9 crop above the copy, padding and title size step down, and the copy
area scrolls within a 90dvh cap.

Sizing is driven by container queries rather than viewport breakpoints, so
the card also stacks inside a narrow agent panel on a wide screen. Above the
threshold it keeps the designed 1040x543 with 555/483 columns.

The close button moves onto the card so it stays top-right in both layouts,
picking up a scrim while it overlaps the video.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the arbitrary rounded-[14px] and rounded-[13px] with rounded-agent,
the radius token the extension already uses for popovers, coach marks and
message bubbles. Buttons keep rounded-lg, which is the 8px token the design
calls for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Swaps the extension-local rounded-agent for the shared radius scale: the card
takes the largest step and the video the next one down, so the inner corner
stays tighter than the outer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Card takes rounded-2xl and the video rounded-xl, one step down, which lands
within a couple of pixels of the 14/13 in the design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Consent has to be answered, so the dismiss affordance is gone along with its
close emit and translation key. Reject is the way out.

The three actions now use @/components/ui/button/Button rather than hand-rolled
markup: inverted for accept, secondary for reject, textonly for the docs link.
Its md size reproduces the design's 57x32, 52x32 and 118x32 exactly, which the
hand-rolled padding did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Clicking Ask Comfy Agent now shows the consent card before the panel opens,
and only the first time: useAgentConsent records the answer in localStorage
under Comfy.AgentPanel.consentAccepted and lets later clicks straight through.
Accept opens the panel, reject leaves it closed.

The card is shown as a headless reka dialog with every dismissal path off, so
it can only be answered, matching the removal of its close button. Closing an
open panel is unaffected.

agentPanelStore.open was defined but never returned; it is exported now that
the consent callback needs it, so opening keeps its topbar_button telemetry
instead of going through toggle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dialog used a w-fit content box copied from the queue-clear-history
precedent, whose content has an intrinsic width. This card sizes itself with
w-full, so it collapsed to zero and the dialog showed nothing but its scrim.
The content box now carries an explicit width, and size xl stops the default
md variant capping it at 576px.

Also points Read the docs at the in-app agent page and supplies the video, so
the media pane no longer falls back to its placeholder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
?agentConsent=always re-asks on every panel open and does not record the
answer, so the card can be shown repeatedly without clearing localStorage by
hand. Without the parameter the first-run behaviour is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The video sits on its own rounded plate and does not need the white hairline.
The no-video placeholder loses it too, taking a raised fill instead so the
empty state still reads as a region.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Report the entry click once it has an outcome, so declining consent is no
  longer counted as a panel open.
- Let Escape decline. The mask stays inert, but a card that fails to render no
  longer traps the user behind an undismissable modal.
- Load the card with defineAsyncComponent so cloud-only UI stays out of the
  bundles every distribution ships.
- Key consent by user id, so a second account on the same browser profile is
  asked in its own right rather than inheriting the first user's answer.
- Drop role/aria-modal from the card; reka's DialogContent already supplies
  both, and the pair nested a dialog inside a dialog.
- Hide the docs button when no docsUrl is supplied instead of rendering a
  control that does nothing.

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

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 03a80629-0fff-4d7d-aeef-2cd411a064c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

🎭 Playwright: ❌ 1728 passed, 51 failed · 2 flaky

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 1717 / ❌ 46 / ⚠️ 2 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 8 / ❌ 5 / ⚠️ 0 / ⏭️ 0)

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 08/23/2026, 01:46:31 AM UTC

Links

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 85.9 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 80.2 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 88.5 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 96.5 MB heap
large-graph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 98.2 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 97.8 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 97.9 MB heap
legacy-node-drag: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 89.0 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 100.4 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 96.4 MB heap
subgraph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 85.9 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 102.9 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 146ms TBT · 115.6 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 91.5 MB heap
vue-large-graph-idle: · 54.5 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 190.6 MB heap
vue-large-graph-pan: · 55.4 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 110ms TBT · 201.3 MB heap
workflow-execution: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 86.4 MB heap

ℹ️ No baseline found — significance unavailable.

Absolute values
Metric Value
canvas-idle: avg frame time 17ms
canvas-idle: p95 frame time 17ms
canvas-idle: layout duration 0ms
canvas-idle: style recalc duration 9ms
canvas-idle: layout count 0
canvas-idle: style recalc count 10
canvas-idle: task duration 506ms
canvas-idle: script duration 8ms
canvas-idle: TBT 0ms
canvas-idle: heap used 85.9 MB
canvas-idle: DOM nodes 19
canvas-idle: event listeners 6
canvas-mouse-sweep: avg frame time 17ms
canvas-mouse-sweep: p95 frame time 17ms
canvas-mouse-sweep: layout duration 5ms
canvas-mouse-sweep: style recalc duration 43ms
canvas-mouse-sweep: layout count 14
canvas-mouse-sweep: style recalc count 77
canvas-mouse-sweep: task duration 928ms
canvas-mouse-sweep: script duration 125ms
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: heap used 80.2 MB
canvas-mouse-sweep: DOM nodes 62
canvas-mouse-sweep: event listeners 5
canvas-zoom-sweep: avg frame time 17ms
canvas-zoom-sweep: p95 frame time 17ms
canvas-zoom-sweep: layout duration 1ms
canvas-zoom-sweep: style recalc duration 19ms
canvas-zoom-sweep: layout count 6
canvas-zoom-sweep: style recalc count 32
canvas-zoom-sweep: task duration 400ms
canvas-zoom-sweep: script duration 12ms
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: heap used 88.5 MB
canvas-zoom-sweep: DOM nodes 78
canvas-zoom-sweep: event listeners 19
dom-widget-clipping: avg frame time 17ms
dom-widget-clipping: p95 frame time 17ms
dom-widget-clipping: layout duration 0ms
dom-widget-clipping: style recalc duration 9ms
dom-widget-clipping: layout count 0
dom-widget-clipping: style recalc count 12
dom-widget-clipping: task duration 368ms
dom-widget-clipping: script duration 56ms
dom-widget-clipping: TBT 0ms
dom-widget-clipping: heap used 96.5 MB
dom-widget-clipping: DOM nodes 19
dom-widget-clipping: event listeners 1
large-graph-idle: avg frame time 17ms
large-graph-idle: p95 frame time 17ms
large-graph-idle: layout duration 0ms
large-graph-idle: style recalc duration 9ms
large-graph-idle: layout count 0
large-graph-idle: style recalc count 8
large-graph-idle: task duration 632ms
large-graph-idle: script duration 18ms
large-graph-idle: TBT 0ms
large-graph-idle: heap used 98.2 MB
large-graph-idle: DOM nodes 15
large-graph-idle: event listeners 6
large-graph-pan: avg frame time 17ms
large-graph-pan: p95 frame time 17ms
large-graph-pan: layout duration 0ms
large-graph-pan: style recalc duration 14ms
large-graph-pan: layout count 0
large-graph-pan: style recalc count 68
large-graph-pan: task duration 1223ms
large-graph-pan: script duration 353ms
large-graph-pan: TBT 0ms
large-graph-pan: heap used 97.8 MB
large-graph-pan: DOM nodes 13
large-graph-pan: event listeners 5
large-graph-zoom: avg frame time 17ms
large-graph-zoom: p95 frame time 17ms
large-graph-zoom: layout duration 13ms
large-graph-zoom: style recalc duration 16ms
large-graph-zoom: layout count 60
large-graph-zoom: style recalc count 66
large-graph-zoom: task duration 1421ms
large-graph-zoom: script duration 406ms
large-graph-zoom: TBT 0ms
large-graph-zoom: heap used 97.9 MB
large-graph-zoom: DOM nodes 13
large-graph-zoom: event listeners 8
legacy-node-drag: avg frame time 17ms
legacy-node-drag: p95 frame time 17ms
legacy-node-drag: layout duration 0ms
legacy-node-drag: style recalc duration 10ms
legacy-node-drag: layout count 0
legacy-node-drag: style recalc count 45
legacy-node-drag: task duration 1526ms
legacy-node-drag: script duration 481ms
legacy-node-drag: TBT 0ms
legacy-node-drag: heap used 89.0 MB
legacy-node-drag: DOM nodes 10
legacy-node-drag: event listeners 186
minimap-idle: avg frame time 17ms
minimap-idle: p95 frame time 17ms
minimap-idle: layout duration 0ms
minimap-idle: style recalc duration 7ms
minimap-idle: layout count 0
minimap-idle: style recalc count 8
minimap-idle: task duration 623ms
minimap-idle: script duration 17ms
minimap-idle: TBT 0ms
minimap-idle: heap used 100.4 MB
minimap-idle: DOM nodes 15
minimap-idle: event listeners 4
subgraph-dom-widget-clipping: avg frame time 17ms
subgraph-dom-widget-clipping: p95 frame time 17ms
subgraph-dom-widget-clipping: layout duration 0ms
subgraph-dom-widget-clipping: style recalc duration 10ms
subgraph-dom-widget-clipping: layout count 0
subgraph-dom-widget-clipping: style recalc count 46
subgraph-dom-widget-clipping: task duration 403ms
subgraph-dom-widget-clipping: script duration 119ms
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: heap used 96.4 MB
subgraph-dom-widget-clipping: DOM nodes 18
subgraph-dom-widget-clipping: event listeners 7
subgraph-idle: avg frame time 17ms
subgraph-idle: p95 frame time 17ms
subgraph-idle: layout duration 0ms
subgraph-idle: style recalc duration 9ms
subgraph-idle: layout count 0
subgraph-idle: style recalc count 10
subgraph-idle: task duration 479ms
subgraph-idle: script duration 8ms
subgraph-idle: TBT 0ms
subgraph-idle: heap used 85.9 MB
subgraph-idle: DOM nodes 20
subgraph-idle: event listeners 6
subgraph-mouse-sweep: avg frame time 17ms
subgraph-mouse-sweep: p95 frame time 17ms
subgraph-mouse-sweep: layout duration 6ms
subgraph-mouse-sweep: style recalc duration 38ms
subgraph-mouse-sweep: layout count 18
subgraph-mouse-sweep: style recalc count 78
subgraph-mouse-sweep: task duration 815ms
subgraph-mouse-sweep: script duration 92ms
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: heap used 102.9 MB
subgraph-mouse-sweep: DOM nodes 65
subgraph-mouse-sweep: event listeners 5
subgraph-transition-enter: avg frame time 17ms
subgraph-transition-enter: p95 frame time 17ms
subgraph-transition-enter: layout duration 14ms
subgraph-transition-enter: style recalc duration 31ms
subgraph-transition-enter: layout count 14
subgraph-transition-enter: style recalc count 18
subgraph-transition-enter: task duration 1006ms
subgraph-transition-enter: script duration 17ms
subgraph-transition-enter: TBT 146ms
subgraph-transition-enter: heap used 115.6 MB
subgraph-transition-enter: DOM nodes 13673
subgraph-transition-enter: event listeners 2375
viewport-pan-sweep: avg frame time 17ms
viewport-pan-sweep: p95 frame time 17ms
viewport-pan-sweep: layout duration 0ms
viewport-pan-sweep: style recalc duration 41ms
viewport-pan-sweep: layout count 0
viewport-pan-sweep: style recalc count 250
viewport-pan-sweep: task duration 4497ms
viewport-pan-sweep: script duration 1100ms
viewport-pan-sweep: TBT 0ms
viewport-pan-sweep: heap used 91.5 MB
viewport-pan-sweep: DOM nodes -281
viewport-pan-sweep: event listeners -157
vue-large-graph-idle: avg frame time 18ms
vue-large-graph-idle: p95 frame time 17ms
vue-large-graph-idle: layout duration 0ms
vue-large-graph-idle: style recalc duration 0ms
vue-large-graph-idle: layout count 0
vue-large-graph-idle: style recalc count 0
vue-large-graph-idle: task duration 17278ms
vue-large-graph-idle: script duration 118ms
vue-large-graph-idle: TBT 0ms
vue-large-graph-idle: heap used 190.6 MB
vue-large-graph-idle: DOM nodes -8312
vue-large-graph-idle: event listeners -16368
vue-large-graph-pan: avg frame time 18ms
vue-large-graph-pan: p95 frame time 17ms
vue-large-graph-pan: layout duration 0ms
vue-large-graph-pan: style recalc duration 20ms
vue-large-graph-pan: layout count 0
vue-large-graph-pan: style recalc count 175
vue-large-graph-pan: task duration 21213ms
vue-large-graph-pan: script duration 416ms
vue-large-graph-pan: TBT 110ms
vue-large-graph-pan: heap used 201.3 MB
vue-large-graph-pan: DOM nodes -8312
vue-large-graph-pan: event listeners -16364
workflow-execution: avg frame time 17ms
workflow-execution: p95 frame time 17ms
workflow-execution: layout duration 1ms
workflow-execution: style recalc duration 21ms
workflow-execution: layout count 4
workflow-execution: style recalc count 12
workflow-execution: task duration 104ms
workflow-execution: script duration 8ms
workflow-execution: TBT 0ms
workflow-execution: heap used 86.4 MB
workflow-execution: DOM nodes 119
workflow-execution: event listeners 73
Raw data
{
  "timestamp": "2026-08-23T02:04:36.525Z",
  "gitSha": "64538ed3518c84be56eedb8bf56e062f0b8e837e",
  "branch": "uy/agent-consent-card",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2071.9379999999887,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.786,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 474.45000000000005,
      "heapDeltaBytes": 1552916,
      "heapUsedBytes": 91184020,
      "domNodes": 20,
      "jsHeapTotalBytes": 6029312,
      "scriptDurationMs": 6.567,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-idle",
      "durationMs": 2012.2109999999793,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.97,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 537.8539999999999,
      "heapDeltaBytes": 1387596,
      "heapUsedBytes": 88927336,
      "domNodes": 18,
      "jsHeapTotalBytes": 5767168,
      "scriptDurationMs": 9.401,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2061.063000000047,
      "styleRecalcs": 79,
      "styleRecalcDurationMs": 48.134,
      "layouts": 14,
      "layoutDurationMs": 5.0089999999999995,
      "taskDurationMs": 1007.0300000000001,
      "heapDeltaBytes": -4515896,
      "heapUsedBytes": 82527288,
      "domNodes": 65,
      "jsHeapTotalBytes": 6553600,
      "scriptDurationMs": 133.83499999999998,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1839.8049999999557,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 38.063,
      "layouts": 14,
      "layoutDurationMs": 5.0760000000000005,
      "taskDurationMs": 849.1650000000001,
      "heapDeltaBytes": -3778972,
      "heapUsedBytes": 85734796,
      "domNodes": 59,
      "jsHeapTotalBytes": 6029312,
      "scriptDurationMs": 115.40400000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1721.1230000000342,
      "styleRecalcs": 33,
      "styleRecalcDurationMs": 17.095,
      "layouts": 6,
      "layoutDurationMs": 1.2049999999999996,
      "taskDurationMs": 362.702,
      "heapDeltaBytes": 4360304,
      "heapUsedBytes": 93830108,
      "domNodes": 79,
      "jsHeapTotalBytes": 6291456,
      "scriptDurationMs": 9.312,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1730.2899999999681,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 20.533,
      "layouts": 6,
      "layoutDurationMs": 1.3490000000000002,
      "taskDurationMs": 436.56300000000005,
      "heapDeltaBytes": 4530700,
      "heapUsedBytes": 91758544,
      "domNodes": 77,
      "jsHeapTotalBytes": 5505024,
      "scriptDurationMs": 13.849000000000002,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 575.4829999999629,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 8.232000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 353.613,
      "heapDeltaBytes": 11818396,
      "heapUsedBytes": 101312040,
      "domNodes": 18,
      "jsHeapTotalBytes": 5767168,
      "scriptDurationMs": 54.964,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 596.1469999999736,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 10.01,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 381.901,
      "heapDeltaBytes": 11556316,
      "heapUsedBytes": 101070788,
      "domNodes": 20,
      "jsHeapTotalBytes": 5767168,
      "scriptDurationMs": 57.62400000000001,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2001.2859999999932,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.639999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 553.28,
      "heapDeltaBytes": -2055816,
      "heapUsedBytes": 104103580,
      "domNodes": 16,
      "jsHeapTotalBytes": 6397952,
      "scriptDurationMs": 16.080000000000002,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2029.739999999947,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 10.645999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 710.469,
      "heapDeltaBytes": -2679076,
      "heapUsedBytes": 101833072,
      "domNodes": 14,
      "jsHeapTotalBytes": 5341184,
      "scriptDurationMs": 20.208000000000006,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666696,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2117.8110000000174,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 13.202000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1147.538,
      "heapDeltaBytes": -4683612,
      "heapUsedBytes": 102433172,
      "domNodes": 14,
      "jsHeapTotalBytes": 6852608,
      "scriptDurationMs": 332.332,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2437.5850000000128,
      "styleRecalcs": 67,
      "styleRecalcDurationMs": 14.090999999999996,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1299.411,
      "heapDeltaBytes": -4640132,
      "heapUsedBytes": 102594900,
      "domNodes": 12,
      "jsHeapTotalBytes": 7376896,
      "scriptDurationMs": 373.08000000000004,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3129.4690000000287,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 14.925,
      "layouts": 60,
      "layoutDurationMs": 11.937999999999999,
      "taskDurationMs": 1342.943,
      "heapDeltaBytes": -4631688,
      "heapUsedBytes": 104021796,
      "domNodes": 12,
      "jsHeapTotalBytes": 6922240,
      "scriptDurationMs": 388.498,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3413.7320000000955,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 17.653,
      "layouts": 60,
      "layoutDurationMs": 13.423,
      "taskDurationMs": 1498.869,
      "heapDeltaBytes": -5039308,
      "heapUsedBytes": 101282608,
      "domNodes": 14,
      "jsHeapTotalBytes": 6914048,
      "scriptDurationMs": 423.703,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "legacy-node-drag",
      "durationMs": 2493.134999999995,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 9.879999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1460.358,
      "heapDeltaBytes": -17892628,
      "heapUsedBytes": 89768012,
      "domNodes": 12,
      "jsHeapTotalBytes": 7176192,
      "scriptDurationMs": 462.97200000000004,
      "eventListeners": 186,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "legacy-node-drag",
      "durationMs": 2770.5439999999726,
      "styleRecalcs": 44,
      "styleRecalcDurationMs": 9.514000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1591.364,
      "heapDeltaBytes": -11061604,
      "heapUsedBytes": 96935824,
      "domNodes": 8,
      "jsHeapTotalBytes": 8912896,
      "scriptDurationMs": 499.66200000000003,
      "eventListeners": 186,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333338,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "minimap-idle",
      "durationMs": 1994.6379999999522,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.147,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 555.568,
      "heapDeltaBytes": -1108368,
      "heapUsedBytes": 106524912,
      "domNodes": 16,
      "jsHeapTotalBytes": 6397952,
      "scriptDurationMs": 15.177000000000003,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2019.8459999999159,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 7.361,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 691.0239999999999,
      "heapDeltaBytes": -1683864,
      "heapUsedBytes": 104104148,
      "domNodes": 14,
      "jsHeapTotalBytes": 5603328,
      "scriptDurationMs": 18.239,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 588.9680000000226,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 10.750999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 389.44100000000003,
      "heapDeltaBytes": 12873564,
      "heapUsedBytes": 102619448,
      "domNodes": 20,
      "jsHeapTotalBytes": 6291456,
      "scriptDurationMs": 116.77000000000001,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 604.3240000000196,
      "styleRecalcs": 45,
      "styleRecalcDurationMs": 10.184999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 416.53999999999996,
      "heapDeltaBytes": 12109856,
      "heapUsedBytes": 99446020,
      "domNodes": 16,
      "jsHeapTotalBytes": 6029312,
      "scriptDurationMs": 120.85900000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1998.6290000000508,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.66,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 437.0280000000001,
      "heapDeltaBytes": 1729688,
      "heapUsedBytes": 91133988,
      "domNodes": 20,
      "jsHeapTotalBytes": 6029312,
      "scriptDurationMs": 6.435,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2037.951000000021,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.075,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 521.843,
      "heapDeltaBytes": 1635588,
      "heapUsedBytes": 89021264,
      "domNodes": 20,
      "jsHeapTotalBytes": 6291456,
      "scriptDurationMs": 10.144,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1689.1219999999976,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 36.232,
      "layouts": 18,
      "layoutDurationMs": 6.341,
      "taskDurationMs": 762.642,
      "heapDeltaBytes": 19654796,
      "heapUsedBytes": 109065372,
      "domNodes": 65,
      "jsHeapTotalBytes": 6291456,
      "scriptDurationMs": 89.41399999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1738.0809999999656,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 40.137,
      "layouts": 18,
      "layoutDurationMs": 6.300999999999999,
      "taskDurationMs": 866.514,
      "heapDeltaBytes": 19648948,
      "heapUsedBytes": 106781180,
      "domNodes": 64,
      "jsHeapTotalBytes": 5505024,
      "scriptDurationMs": 94.60099999999998,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1434.1699999999946,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 30.913999999999998,
      "layouts": 14,
      "layoutDurationMs": 14.154999999999998,
      "taskDurationMs": 1005.8880000000001,
      "heapDeltaBytes": 22544768,
      "heapUsedBytes": 121257916,
      "domNodes": 13673,
      "jsHeapTotalBytes": 2097152,
      "scriptDurationMs": 17.408999999999995,
      "eventListeners": 2375,
      "totalBlockingTimeMs": 146,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8330.196999999998,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 37.204,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4241.210000000001,
      "heapDeltaBytes": -13526464,
      "heapUsedBytes": 90747644,
      "domNodes": -280,
      "jsHeapTotalBytes": 1662976,
      "scriptDurationMs": 1058.2289999999998,
      "eventListeners": -173,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8915.217999999983,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 44.331999999999994,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4752.928,
      "heapDeltaBytes": -3221908,
      "heapUsedBytes": 101122376,
      "domNodes": -282,
      "jsHeapTotalBytes": 614400,
      "scriptDurationMs": 1141.5710000000001,
      "eventListeners": -141,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 17849.10500000001,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16872.479,
      "heapDeltaBytes": -22899644,
      "heapUsedBytes": 199148760,
      "domNodes": -8312,
      "jsHeapTotalBytes": 15990784,
      "scriptDurationMs": 113.896,
      "eventListeners": -16369,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.333333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 18586.787999999957,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17683.587000000003,
      "heapDeltaBytes": -24897840,
      "heapUsedBytes": 200635056,
      "domNodes": -8312,
      "jsHeapTotalBytes": 18317312,
      "scriptDurationMs": 122.40299999999999,
      "eventListeners": -16367,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.333333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 20936.653999999977,
      "styleRecalcs": 171,
      "styleRecalcDurationMs": 17.246000000000038,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20273.133,
      "heapDeltaBytes": -20901828,
      "heapUsedBytes": 206855880,
      "domNodes": -8312,
      "jsHeapTotalBytes": 12742656,
      "scriptDurationMs": 395.24,
      "eventListeners": -16367,
      "totalBlockingTimeMs": 47,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 22830.362000000037,
      "styleRecalcs": 179,
      "styleRecalcDurationMs": 23.363999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 22152.169,
      "heapDeltaBytes": -3761088,
      "heapUsedBytes": 215258252,
      "domNodes": -8312,
      "jsHeapTotalBytes": 15396864,
      "scriptDurationMs": 437.291,
      "eventListeners": -16361,
      "totalBlockingTimeMs": 173,
      "frameDurationMs": 18.333333333333332,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "workflow-execution",
      "durationMs": 113.21299999997336,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 14.956,
      "layouts": 3,
      "layoutDurationMs": 0.7440000000000001,
      "taskDurationMs": 79.804,
      "heapDeltaBytes": 3173184,
      "heapUsedBytes": 89697936,
      "domNodes": 114,
      "jsHeapTotalBytes": 524288,
      "scriptDurationMs": 6.486000000000001,
      "eventListeners": 49,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 459.3199999999342,
      "styleRecalcs": 16,
      "styleRecalcDurationMs": 27.878000000000004,
      "layouts": 4,
      "layoutDurationMs": 1.704,
      "taskDurationMs": 128.415,
      "heapDeltaBytes": 4988144,
      "heapUsedBytes": 91473036,
      "domNodes": 123,
      "jsHeapTotalBytes": 524288,
      "scriptDurationMs": 9.441999999999998,
      "eventListeners": 97,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    }
  ]
}

Removed at the implementing developer's request; their workflow generates the
tests for this feature.

The mock and expectation changes in WorkflowTabs.test.ts stay: the consent gate
calls agentPanelStore.open instead of toggle, so the pre-existing entry-button
test fails without them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the risk:R3 PR risk grade (advisory shadow check; grader-owned) label Aug 23, 2026
@uytieu uytieu added preview Design Used to request Product feedback on design decisions and removed risk:R3 PR risk grade (advisory shadow check; grader-owned) labels Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Design Used to request Product feedback on design decisions preview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant