Skip to content

Report chart: split a series by a second dataset dimension #27974

Closed

Description

@objectstack-fleet

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

Filing gate: ③ triage-directed. The order on #27430 (rows 2–4) said: TAKE what is ui-only, CARD the rest. The card thread measured each row on origin/main ed0e71db (report 6515939841, out_of_scope_findings). Row 2 is taken in PR #27972.
Reach: any report that wants a bar per X split by Y. HotCRM opportunity_funnel_owner_stage (a bar per owner, split by stage); hotcrm#2379 turned it into an owner × stage matrix as the workaround (#27430 row 4).
Reader who acts: domain:spec first (the key; Metadata-protocol added), then domain:ui (plugin-report).
Duplicate search: search_issues "report chart series second dimension groupBy stacked bar ReportChartSchema" → #27563 and #15734, both closed and about other gaps.

Observed

ReportChartSchema (packages/spec ui/report.zod.ts) extends ChartConfig with one xAxis dimension and one yAxis measure, and DatasetReportRenderer queries exactly chart.xAxis × chart.yAxis. A report therefore cannot draw a bar per owner split by stage. The dashboard dataset path already pivots a second dimension into grouped series (buildChartSeries in ui/packages/core), so the renderer half can reuse it.

Wanted

A declared series dimension key on ReportChartSchema (for example series or groupBy, a dimension name validated against the report's dataset). The report query is widened to it, and the renderer pivots through buildChartSeries. The ui half follows as a domain:ui card once the key ships.

Dedupe words: report chart series, second dimension, groupBy series, stacked bar report, ReportChartSchema.

Related: #27430 (row 4), #27563.


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    pm:queueReady for the PM dispatch loop
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 30, 2026
  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D

    Claim: PM loop round 2
    Session: session_01L9qibx5r5nhDMwpRs4SL6D
    Account: os-tesla
    Branch: claude/project-thread-1rmsao (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27974
    Domain: domain:spec
    Seat: domain:spec#2
    File surface: packages/spec/src/ui/report.zod.ts (ReportChartSchema, one series dimension) and the dataset-dimension check that validates xAxis (named in the report), tests, regenerated spec artefacts and reference docs, changeset (@objectstack/spec minor). The ui half is a follow-up domain:ui card the seat files when the key lands; ⛔ no ui/** and no governed path (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: TIER_DEFAULT (Clause-②: yes ⇒ default judgment tier to build; the contract review at CONTRACT_REVIEW_TIER is the seat's own, on the maintainer's 「你有 fable,可以自己契约复审」), effort: high
    Clause-②: yes
    Metadata-protocol: added
    Responsibility: n/a — not a defect card
    Thread-read: none
    Premise: holds (re-read at origin/main 9f13ee81; the declaration named on the card still lacks the key)
    Serial constraints cleared: none (no open PR touches report.zod.ts)
    Ruling-ref: none on the shape; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); the card's filing gate ③ (triage-directed)

    Dispatched 2026-10-30T15:10Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#2 · os-tesla · session_01L9qibxKEFRdVkHHwAWLUSE

    os-dev-report

    Draft PR #28191 at head 061a780d.

    {
      "issue": 27974,
      "status": "done",
      "branch": "claude/project-thread-1rmsao",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28191",
      "session": "session_01L9qibxKEFRdVkHHwAWLUSE",
      "premise_still_valid": true,
      "summary": "ReportChartSchema declares one optional key, seriesBy: a second dimension of the report's bound dataset that splits the one yAxis measure into a series per value (the report spelling of a dashboard widget's two-entry dimensions selection). validateChartBindings (packages/lint/src/validate-chart-bindings.ts, the check that resolves chart.xAxis) resolves chart.seriesBy through the same dimensionRef, so a misspelled one is chart-dimension-unknown at os validate / os build / the report publish door; with seriesBy set, chart.series[].name overrides name dimension values and are not judged against the measure. Liveness: the report chart row now drills (childrenDefault live), seriesBy is planned with authorWarn, the walked aria tombstone is dead, and the three nested containers drilling surfaced are recorded in the undrilled baseline in place of report/chart. Spec half only; no ui/** edits.",
      "tests": "head 061a780d. spec vitest src/ui + scripts/liveness: Test Files 176/176, Tests 6227/6227. lint test (full): Test Files 178/178, Tests 7646/7646. typecheck spec and lint: lock VERDICT command-exit 0. Ablations via scripts/ablation-replace.mjs (lint suite reads src, no dist): seriesBy dimensionRef line deleted -> 1 failed/80; seriesBy series guard reverted -> 1 failed/80; file restored, hash-object == HEAD blob. Author-warning probe: lintLivenessProperties gives one liveness-planned-property finding on a report setting chart.seriesBy, none on the control.",
      "gates": "dispatch-gates --commands at 061a780d: 106 families, run with stdin from /dev/null, exit codes recorded before any pipe (record: /mnt/project-files/spec-seat-2/r2026-10-30/out/27974-gates-ran-061a780d.list). --ran: 106 derived, 106 run, 0 NOT MEASURED, 0 unrun, 0 red. First pass exit 3 on four, re-run after the prerequisite, all exit 0: check-plugin-teardown-shape --self-test (pinned control 621a4876 fetched into the shallow clone), spec check:skill-examples, check:dual-build-cjs-loads, check:lean-entry-closure (after a full packages/** build). spec check:generated: 14/14 up to date; check:liveness exit 0. CI check-runs on #28191: in_progress at report time.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "line_budget": "+135 / -8 across 12 files vs merge base 9f13ee81",
      "files_changed": [
        ".changeset/27974-report-chart-series-by.md",
        "content/docs/references/ui/report.mdx",
        "packages/lint/src/rule-explanations.ts",
        "packages/lint/src/validate-chart-bindings.test.ts",
        "packages/lint/src/validate-chart-bindings.ts",
        "packages/spec/authorable-surface/ui.json",
        "packages/spec/liveness/report.json",
        "packages/spec/liveness/state-counts/report.md",
        "packages/spec/scripts/liveness/undrilled-containers.baseline.json",
        "packages/spec/src/ui/chart.test.ts",
        "packages/spec/src/ui/report.test.ts",
        "packages/spec/src/ui/report.zod.ts"
      ],
      "deviations": [
        "Changeset moves @objectstack/lint minor beside @objectstack/spec minor (the order names spec only): the lint diff adds a finding on a new key, so it publishes.",
        "Liveness ledger edits beyond the order's file surface (report.json chart drill, undrilled-containers baseline swap of report/chart for its three nested containers, state-counts regen): the chart row's blanket live verdict would otherwise vouch for seriesBy, which no renderer reads yet.",
        "Assignee/reviewer not written on the PR (FRAME forbids it; the session prompt's best-effort assign step skipped)."
      ],
      "decision_for_maintainer": "Shape: a new optional seriesBy key. Not taken: reusing ChartConfig `series` (already the per-series presentation override array), `groupBy` (the X-axis category on the react ObjectChart aggregate and the lane field on kanban), widening xAxis to a list (changes a one-string key every reader uses), reusing the report's `columns` (ties the chart split to the matrix table).",
      "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); 0 writes",
      "api_writes": "2 — relay pr_create (objectstack-ai/objectstack#28191, read back identical) and this os-dev-report comment via post-stamped",
      "open_questions": [],
      "out_of_scope_findings": [
        "ui half of #27974 · carrier: the seat — DatasetReportChart (ui/packages/plugin-report/src/DatasetReportRenderer.tsx) queries [xAxis] x [yAxis] and passes one dimension to buildChartSeries; add seriesBy to the query dimensions, pass [xAxis, seriesBy], match chart.series[].name against the pivoted series keys, flip the chart.seriesBy liveness row to live · dedupe words: report chart seriesBy renderer, DatasetReportChart split series",
        "Skills-docs follow-up · carrier: the seat — skills/objectstack-ui/rules/dashboards.md report example teaches chart xAxis/yAxis; teach seriesBy with the renderer half",
        "noted, not filed: report/chart liveness note still says sub-key `groupBy` stays experimental; ReportChartSchema has no groupBy key (stale prose in the row, left untouched)"
      ]
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions