Skip to content

dashboards: a sankey widget with two dimensions silently draws only the first category's flows #27460

Closed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcVkGJizGH7raJHhAvL

Filing gate: ① a product defect (silent data loss in a chart) found by a QA run (off-clause probe, RUNNER rule 8).

Reproduction

  1. Boot the showcase. In a writable package, define a dataset over an object with two categorical fields (e.g. status and category, several values each) and a count measure.
  2. Author a dashboard widget {"type":"sankey","dataset":"DATASET","dimensions":["status","category"],"values":["task_count"]} and a pivot over the same dataset for comparison. Publish and open the dashboard.
  3. Expected: a sankey whose flows cover every first-dimension value (or a stated refusal of a two-dimension sankey).
  4. Actual: a single root node (the first category value, e.g. "Alpha") whose flows (3/2/2) are that value's subset only. The pivot shows Alpha/Beta/Gamma. Nothing says data was dropped.

Mechanism

The sankey arm of AdvancedChartImpl.tsx uses series[0] only. A two-dimension query yields one series per first-dimension value, and all but the first are discarded.

QA-source: #27441 · studio-authoring.dashboard-widget-and-filter-types · observed beside the clauses


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 经营报表 | none | P②

    Triage: p2 · domain:ui · area:reports · 2026-10-29T08:02Z

    ⛔ Not a claim.

    • Read fresh. A sankey widget with two dimensions draws only the first category's flows. That is silent data loss in a chart. From the 18.3.0 RC run ([sweep] v18.3.0 release test sweep #27377 §5); it is retested on the next wave.
    • Either draw every flow, or refuse a shape the renderer cannot draw, at publish, with a prescription. ⛔ It never draws a partial chart silently.
    • bug, pm:queue.

    Generated by Claude Code

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    Dispatch order · domain:ui#1 · session_01L6bhgXU26UBEjd1KKHgNEx · 2026-10-29T11:42Z

    You are the os-dev developer agent for exactly ONE card: #27460, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/27460" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.

    Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "ui 席 1" one message with your branch name and wait. Worktree objectstack-issue-27460 on that branch from a fresh origin/main; pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).

    Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…}], draft forced, ⛔ no assignees and ⛔ no review request: the seat hands the PR to a person after its ACCEPT; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=27460 --file=F --json run ONCE with output captured to a file (timestamps only as {{NOW}} / {{WAS:…}}; first line the signature domain:ui#1 · os-sales · <your session>). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的), Fixes #27460, Clause-②: and Metadata-protocol: lines as in the claim. Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran. Changeset in the root .changeset/ named 27460-<slug>.md when a published package changes. ⛔ ui/.github/** and the root ci.yml are out of bounds. Same-day churn: work on the merged code; snippets the card quotes may have moved, check current main first. Hand back: when the PR and report are up, send the seat thread "ui 席 1" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.

    Ruling (cannot be re-ruled)

    No maintainer ruling. Triage direction (on the card), verbatim: 「Either draw every flow, or refuse a shape the renderer cannot draw, at publish, with a prescription. ⛔ It never draws a partial chart silently.」

    PM mechanism assumptions (measured read-only on origin/main 57c51aab4d; re-measure)

    • ui/packages/plugin-charts/src/AdvancedChartImpl.tsx ~2709–2731 builds the sankey from series[0]?.dataKey only (one root plus one link per row); nothing handles series.length > 1 (the only such check, ~2887, is the scatter arm).
    • ui/packages/plugin-dashboard/src/DatasetWidget.tsx ~2066 / ~2145: two dimensions with one measure pivot the second dimension into series, which the sankey arm then drops.
    • The spec's checkDashboardWidgetChartMeasureArity (packages/spec/src/ui/dashboard.zod.ts ~890–960, SINGLE_SERIES_CHART_TYPES ~808) refuses multiple MEASURES on sankey but not multiple dimensions; DatasetWidget.tsx ~501 mirrors the set.
    • Not verified: whether pie / donut / funnel / treemap share the same series[0] drop. Measure each and report; fold any that share this exact defect shape and fix into this PR only if the same fix covers them, otherwise list them in Acceptance notes.

    PM suggested route (optional; measurement wins)

    • Prefer "draw every flow": two dimensions on a sankey are source → target; build nodes from both dimensions and one link per (dim1, dim2) cell with the measure as the value. A refusal is the route only for shapes the renderer still cannot draw (three or more dimensions?), and then it belongs at publish with a prescription (that would touch packages/spec/**, which is the spec lane: if you measure that the publish-time refusal is needed, stop and report it instead of editing spec).

    Scope

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    Claim: PM loop round 1 · 2026-10-29T11:47Z
    Session: session_01L6bhgXU26UBEjd1KKHgNEx (seat; implementation by card thread session session_01L6bhgWJECm7uQEkPAHKqat, mode:thread)
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-4o2vqj (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
    Worktree: objectstack-issue-27460 in the card thread's clone
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/plugin-charts/src/AdvancedChartImpl.tsx (sankey arm; other single-series arms only if the same fix covers the same defect), ui/packages/plugin-dashboard/src/DatasetWidget.tsx only if the pivot must hand both dimensions, tests beside, changeset; ⛔ packages/spec/** (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (seat-run dispatch-gates --tier on the paths: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: dashboard chart renderer (this lane) | none: no path draws the second dimension today | every dashboard author who sets two dimensions on a sankey widget (silent data loss)
    Thread-read: 6495728211
    Premise: holds — on origin/main 57c51aab4d the sankey arm reads series[0]?.dataKey only and DatasetWidget pivots the second dimension into series (seat premise check).
    Serial constraints cleared: open PR #27340 (os-bill) edits DatasetWidget.tsx ~1883 (table cell format): ordinary concurrency; no open PR touches plugin-charts.
    Resume-from: none

    Dispatch order: comment 6495728211.

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWJECm7uQEkPAHKqat
    os-dev-report

    Report at 2026-10-29T13:03Z for PR #27560, head 13e084eb.

    {
      "issue": 27460,
      "status": "done",
      "branch": "claude/project-thread-4o2vqj",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27560",
      "head": "13e084eb896ed91a2f7a0aa052f884d57f4a6aea",
      "session": "session_01L6bhgWJECm7uQEkPAHKqat",
      "premise_still_valid": true,
      "summary": "Sankey arm: two or more non-comparison series are the groups of a pivoted second dimension (the spec refuses a second measure on sankey), drawn as source nodes (rows, xAxisKey) to target nodes (series) with one link per carried cell above zero (buildSankeyCellFlows). Absent cells are no flow and no omission; carried non-positive cells reuse chart.sankeyOmittedRows once per target column; no positive cell reuses the no-positive-flow refusal. A link click drills to its cell; node clicks do not drill. DatasetWidget hands a sankey only its rendered measure, so a stored multi-measure sankey is not drawn as groups and the dropped-measure warning stays true. One-series sankey unchanged.",
      "files_changed": [
        ".changeset/27460-sankey-two-dimensions.md",
        "ui/packages/plugin-charts/src/AdvancedChartImpl.tsx",
        "ui/packages/plugin-charts/src/AdvancedChartImpl.sankeyTwoDimensions-27460.test.tsx",
        "ui/packages/plugin-dashboard/src/DatasetWidget.tsx",
        "ui/packages/plugin-dashboard/src/__tests__/DatasetWidget.sankeyTwoDimensions-27460.test.tsx"
      ],
      "line_budget": "+465 / -5 over 5 files at 13e084eb (dispatch-gates change set vs merge base aef631b80), under the 3000 threshold",
      "tests": "13e084eb: new files 9/9 passed (pnpm --dir ui exec vitest run on both new files). pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-charts/ packages/plugin-dashboard/ : Test Files 294 passed (294), Tests 3175 passed, 15 skipped; lock VERDICT command-exit 0. type-check: @object-ui/plugin-charts Done; @object-ui/plugin-dashboard VERDICT command-exit 0 (after building its closure). Ablation 1 (source-aliased, no dist): scripts/ablation-replace.mjs anchor 'if (flowSeries.length >= 2) {' 1 -> 0, blob b1e3dd96b956 -> 0efa8d4b9117, 5 of 9 red (first: expected [ 'Alpha → Open: 3', …(2) ], the card's 3/2/2), controls green, restored blob == HEAD, git diff HEAD empty. Ablation 2: chartSeries 'sankey' -> 'ablated' in DatasetWidget: DatasetWidget.singleSeriesMeasures-11417 red 1 of 22 (sankey tie), restored blob == HEAD.",
      "gates": "dispatch-gates --commands at 13e084eb: 35 derived; 34 exit 0; 'pnpm --dir ui exec vitest run scripts/' exit 1 on the full run (1 of 4903: check-vi-mock-inherit.test.ts sweep timed out at 15000ms on a shared box), that file alone 83/83 exit 0. --ran with exit codes: 35 derived, 35 run, 0 NOT-MEASURED (derived zero). CI on 13e084eb: 55 check runs completed, 44 success, 11 skipped, 0 failure; required: Lint & Repo Gates success, TypeScript Type Check success, Test Core success, Build Core skipped, Governed Surface Queue Guard success, Lint UI success, Type Check UI success, Test UI 17/17 success, Build & E2E UI success, Live E2E UI success.",
      "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no without a (narrowing) arm",
      "mcp_calls": "0 GitHub MCP calls (platform subscribe_pr_activity on #27560 only, not a GitHub tool)",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create, run 40876863543, read-back identical, 5696 bytes); this report via post-stamped (POST issues/27460/comments). git push is not a REST write.",
      "deviations": [
        "PR body order follows os-dev (harness attribution, then Fixes #27460, Clause-②, Metadata-protocol, then 维护者速读), not the dispatch's (速读 before Fixes): os-dev wins on conflict.",
        "DatasetWidget.tsx changed for the stored multi-measure sankey (hand only the rendered measure), not for the pivot: without it the renderer would draw a stored second measure as a group and the dropped-measure warning would be false; within the claim's conditional surface.",
        "The shared checkout held the harness branch, so it was detached before 'git worktree add -B claude/project-thread-4o2vqj origin/main'; no uncommitted work there.",
        "First closure build failed on this branch's own type error (SankeyArmNode annotation), fixed in 9e943a84 before any test reading."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: NOT MEASURED by render (read on 13e084eb: the pie/donut, funnel and treemap arms read series[0]?.dataKey only; a bare jsdom render probe was non-deterministic for pie and drew no marks for funnel/treemap) · a dashboard pie/donut/funnel/treemap widget with dimensions [a, b] and one measure draws only the first b group, silently · dedupe words: pie two dimensions, pivot series[0], funnel second dimension, treemap pivot dropped",
        "carrier: none · noted, not filed: buildChartSeries pivots only dimensions[1]; a third dimension collapses into the (d0, d1) cell (last row wins) on every family"
      ]
    }
  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    落地 · 2026-10-29T20:16Z

    读数 结果
    PR #27560 MERGED 2026-10-29T20:03Z,合并提交 9e73c08c8a 在 origin/main 上
    git grep -c "27460" origin/main -- ui/packages/plugin-charts/src/AdvancedChartImpl.tsx 4(阳性对照 import = 8)

    状态:卡已由 PR 关闭,摘 pm:dispatched。范围外发现已立卡 #27563。

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

Metadata

Metadata

Assignees

Labels

area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:uipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions