Skip to content

fix(chatboxes): keep the session flow headers with their columns - #3673

Merged
chelojimenez merged 1 commit into
mainfrom
fix/insights-sankey-header-alignment
Aug 3, 2026
Merged

fix(chatboxes): keep the session flow headers with their columns#3673
chelojimenez merged 1 commit into
mainfrom
fix/insights-sankey-header-alignment

Conversation

@chelojimenez

@chelojimenez chelojimenez commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The SENTIMENT header sits hundreds of pixels from the column it names.

Cause

Two coordinate systems that only agreed by accident. The stage headers were a four-cell CSS grid-cols-4 stretched across the full panel; the diagram is a fixed-width SVG. On a panel wider than the chart the grid keeps stretching and the columns do not, so GOAL / BEHAVIOR / OUTCOME lined up roughly by coincidence and SENTIMENT — the one pinned to the right edge by text-right — did not line up at all.

Fix

Headers are drawn inside the SVG, at the columnX the layout already computes. layoutSankey now echoes those positions back specifically so the headers and the nodes read from one source; sharing a coordinate space is the only arrangement in which they cannot drift.

Also unflipped the last column's labels. They were anchored to the LEFT of their bar — a defensive choice from before the label gutter existed — which by now just painted the text on top of the ribbons arriving at that column and read as a rendering fault. Every column labels to the right, into the gutter already reserved for it. The viewBox widened to 1160 with a 260px gutter so the longest sentiment names ("Neutral, mild curiosity") fit without truncation.

Pinned

  • insights-sankey.test.ts — every node sits on its own column's x, and the layout returns those positions.
  • ChatboxInsightsSankey.test.tsx — four distinct, strictly increasing header positions.

Neither test passes against the old arrangement, since the old headers were not in the SVG at all.

Verification

  • npm run typecheck:client — clean
  • npm run build:inspector — succeeds
  • Full suite — 12,169 passed, 6 skipped

Rendering still wants a real look: jsdom places the text but does not measure it, so whether the longest theme names actually clear the gutter is not something these tests can answer.

🤖 Generated with Claude Code


Note

Low Risk
Insights Sankey presentation-only changes with regression tests; no API, auth, or data handling impact.

Overview
Fixes session flow column headers drifting away from their Sankey columns on wide panels by moving stage titles into the SVG at layout.columnX instead of a full-width CSS grid above a fixed-width chart. layoutSankey now returns columnX so headers and nodes share one coordinate system; diagram content is shifted down by a new HEADER_HEIGHT band.

Sentiment (and all) node labels now anchor to the right of each bar into the label gutter, avoiding text drawn over incoming ribbons. The view widens to 1160px with a 260px gutter for long theme names. Tests lock header x positions and layout columnX echo.

Reviewed by Cursor Bugbot for commit aa2d17f. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes misaligned session flow headers by rendering them inside the SVG at the computed column positions, so headers stay aligned with their columns on any panel width. Also moves the last column's labels to the right and expands the label gutter to prevent overlap.

  • Bug Fixes
    • Render headers inside the SVG using layoutSankey’s columnX; add a header band and translate nodes/links accordingly.
    • layoutSankey now returns columnX so nodes and headers share one coordinate space.
    • Widened viewBox to 1160 with a 260px gutter and label all columns to the right to avoid ribbon overlap; tests pin header and node x-positions.

Written for commit aa2d17f. Summary will update on new commits.

Review in cubic

On a wide panel the SENTIMENT header sat hundreds of pixels from the column it
names. The headers were a four-cell CSS grid stretched across the panel while
the diagram was a fixed-width SVG: two coordinate systems that only agreed when
the panel happened to be the chart's width, so the first three columns lined up
by coincidence and the fourth did not line up at all.

They are drawn inside the SVG now, at the `columnX` the layout already computed.
Sharing one coordinate space is the only arrangement in which a header and its
column cannot drift, and the layout echoes those positions back specifically so
there is one source for both.

The last column also labelled to the LEFT of its bar — a defensive choice from
before the gutter was reserved, which by now just put the text on top of the
ribbons arriving at it. Every column labels to the right, into the space that
was already there for it.

Both are pinned: the layout test asserts every node sits on its column's x, and
the component test asserts four distinct, increasing header positions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Aug 3, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@dosubot dosubot Bot added the bug Something isn't working label Aug 3, 2026
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b9861249-3812-4830-8636-d3404036f33b)

@dosubot

dosubot Bot commented Aug 3, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-09-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about inspector Add Dosu to your team

@chelojimenez

Copy link
Copy Markdown
Contributor Author

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@chelojimenez
chelojimenez merged commit 781f18d into main Aug 3, 2026
13 of 14 checks passed
@chelojimenez
chelojimenez deleted the fix/insights-sankey-header-alignment branch August 3, 2026 16:43
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Internal preview

Preview URL will appear in Railway after the deploy finishes.
Deployed commit: 905cf17
PR head commit: aa2d17f
Backend target: staging fallback.
Access is employee-only in non-production environments.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The Sankey layout now returns column x-coordinates. The diagram renders stage headers inside the SVG using those coordinates. Links and nodes move below the header band. Labels and counts render to the right of all node bars. Tests verify column alignment and ordered header positions. A patch changeset documents the update.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

mcpjam-inspector/client/src/components/chatboxes/ChatboxInsightsSankey.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: ESLint configuration in --config is invalid:

  • Unexpected top-level property "__esModule".

    at ConfigValidator.validateConfigSchema (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2177:19)
    at ConfigArrayFactory._normalizeConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3019:19)
    at ConfigArrayFactory._loadConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2984:21)
    at ConfigArrayFactory.loadFile (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2850:40)
    at createCLIConfigArray (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3660:35)
    at new CascadingConfigArrayFactory (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3735:29)
    at new CLIEngine (/soundcheck/node_modules/eslint/lib/cli-engine/cli-engine.js:617:36)
    at new ESLint (/soundcheck/node_modules/eslint/lib/eslint/eslint.js:430:27)
    at Object.execute (/soundcheck/node_modules/eslint/lib/cli.js:410:24)
    at async main (/soundcheck/node_modules/eslint/bin/eslint.js:152:22)

mcpjam-inspector/client/src/components/chatboxes/__tests__/ChatboxInsightsSankey.test.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: ESLint configuration in --config is invalid:

  • Unexpected top-level property "__esModule".

    at ConfigValidator.validateConfigSchema (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2177:19)
    at ConfigArrayFactory._normalizeConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3019:19)
    at ConfigArrayFactory._loadConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2984:21)
    at ConfigArrayFactory.loadFile (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2850:40)
    at createCLIConfigArray (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3660:35)
    at new CascadingConfigArrayFactory (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3735:29)
    at new CLIEngine (/soundcheck/node_modules/eslint/lib/cli-engine/cli-engine.js:617:36)
    at new ESLint (/soundcheck/node_modules/eslint/lib/eslint/eslint.js:430:27)
    at Object.execute (/soundcheck/node_modules/eslint/lib/cli.js:410:24)
    at async main (/soundcheck/node_modules/eslint/bin/eslint.js:152:22)

mcpjam-inspector/client/src/components/chatboxes/__tests__/insights-sankey.test.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: ESLint configuration in --config is invalid:

  • Unexpected top-level property "__esModule".

    at ConfigValidator.validateConfigSchema (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2177:19)
    at ConfigArrayFactory._normalizeConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3019:19)
    at ConfigArrayFactory._loadConfigData (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2984:21)
    at ConfigArrayFactory.loadFile (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2850:40)
    at createCLIConfigArray (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3660:35)
    at new CascadingConfigArrayFactory (/soundcheck/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3735:29)
    at new CLIEngine (/soundcheck/node_modules/eslint/lib/cli-engine/cli-engine.js:617:36)
    at new ESLint (/soundcheck/node_modules/eslint/lib/eslint/eslint.js:430:27)
    at Object.execute (/soundcheck/node_modules/eslint/lib/cli.js:410:24)
    at async main (/soundcheck/node_modules/eslint/bin/eslint.js:152:22)

  • 1 others

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
mcpjam-inspector/client/src/components/chatboxes/__tests__/insights-sankey.test.ts (1)

212-226: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use STAGE_ORDER as the test source of truth.

The test repeats stage names and their order in two literals, while layoutSankey indexes columnX by STAGE_ORDER. If a stage is added or reordered, this test can skip coverage or assert the wrong column. Iterate over STAGE_ORDER with its index instead.

🤖 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
`@mcpjam-inspector/client/src/components/chatboxes/__tests__/insights-sankey.test.ts`
around lines 212 - 226, Update the “echoes the column positions back for the
headers to share” test to iterate over STAGE_ORDER with each stage’s index,
replacing both repeated stage-name literals. Use that index to compare nodes
against laid.columnX, while preserving the empty-stage skip and existing
assertions.
🤖 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
`@mcpjam-inspector/client/src/components/chatboxes/__tests__/ChatboxInsightsSankey.test.tsx`:
- Around line 302-318: Strengthen the test around the Sankey header-rendering
implementation by adding a stable stage marker to each header element, then
query headers by that marker rather than generic text nodes. In the test “draws
each column header at its own column's x”, map each stage header to its
corresponding node column and assert matching x coordinates, while retaining the
four-header coverage.

In `@mcpjam-inspector/client/src/components/chatboxes/ChatboxInsightsSankey.tsx`:
- Around line 48-50: Update the columnX calculation in ChatboxInsightsSankey so
columns are positioned between explicit left and right bounds, with the final
node ending exactly at VIEW_WIDTH - LABEL_GUTTER. Remove the extra left-margin
effect from the right-edge calculation while preserving the intended left margin
and existing column spacing.

---

Nitpick comments:
In
`@mcpjam-inspector/client/src/components/chatboxes/__tests__/insights-sankey.test.ts`:
- Around line 212-226: Update the “echoes the column positions back for the
headers to share” test to iterate over STAGE_ORDER with each stage’s index,
replacing both repeated stage-name literals. Use that index to compare nodes
against laid.columnX, while preserving the empty-stage skip and existing
assertions.
🪄 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 Plus

Run ID: 428f2b06-605a-414c-94e3-ee51d5da8770

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd9c58 and aa2d17f.

📒 Files selected for processing (5)
  • .changeset/insights-flow-header-alignment.md
  • mcpjam-inspector/client/src/components/chatboxes/ChatboxInsightsSankey.tsx
  • mcpjam-inspector/client/src/components/chatboxes/__tests__/ChatboxInsightsSankey.test.tsx
  • mcpjam-inspector/client/src/components/chatboxes/__tests__/insights-sankey.test.ts
  • mcpjam-inspector/client/src/components/chatboxes/insights-sankey.ts

Comment on lines +302 to +318
it("draws each column header at its own column's x", () => {
// Guards the misalignment that shipped: headers laid out by CSS across the
// full panel while the columns lived in a fixed-width SVG.
renderSankey();
const headers = Array.from(document.querySelectorAll("text")).filter((t) =>
["GOAL", "BEHAVIOR", "OUTCOME", "SENTIMENT"].includes(
(t.textContent ?? "").toUpperCase(),
),
);
expect(headers).toHaveLength(4);
const xs = headers.map((h) => Number(h.getAttribute("x")));
// Strictly increasing, and the last one is nowhere near the right edge —
// it sits over its column, with the label gutter beyond it.
expect(xs).toEqual([...xs].sort((a, b) => a - b));
expect(new Set(xs).size).toBe(4);
});

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert each header against its stage column.

This test checks only that four matching text nodes have unique, sorted x values. It can pass when all headers are shifted together or assigned to the wrong stages. querySelectorAll("text") can also include a node label with the same text as a stage title. Add a stable stage marker to header elements and compare each header’s x with the corresponding node column.

🤖 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
`@mcpjam-inspector/client/src/components/chatboxes/__tests__/ChatboxInsightsSankey.test.tsx`
around lines 302 - 318, Strengthen the test around the Sankey header-rendering
implementation by adding a stable stage marker to each header element, then
query headers by that marker rather than generic text nodes. In the test “draws
each column header at its own column's x”, map each stage header to its
corresponding node column and assert matching x coordinates, while retaining the
four-header coverage.

Comment on lines +48 to +50
const VIEW_WIDTH = 1160;
/** Reserved to the right of the last column for its labels. */
const LABEL_GUTTER = 260;

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reserve the full LABEL_GUTTER after the last node.

The columnX calculation at Lines 111-115 adds the 40px left margin after subtracting LABEL_GUTTER. With VIEW_WIDTH = 1160 and LABEL_GUTTER = 260, the final bar ends at 940, so only 220px remains in the viewBox. The 10px label gap leaves about 210px for the label. Derive the columns between explicit left and right bounds so the final bar ends at VIEW_WIDTH - LABEL_GUTTER.

🤖 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 `@mcpjam-inspector/client/src/components/chatboxes/ChatboxInsightsSankey.tsx`
around lines 48 - 50, Update the columnX calculation in ChatboxInsightsSankey so
columns are positioned between explicit left and right bounds, with the final
node ending exactly at VIEW_WIDTH - LABEL_GUTTER. Remove the extra left-margin
effect from the right-edge calculation while preserving the intended left margin
and existing column spacing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant